<rss version="2.0">
  <channel>
    <title>Drew DeVault&apos;s blog</title>
    <link>https://drewdevault.com</link>
    <description>Drew DeVault&apos;s blog - Blog</description>
    <generator>Zine -- https://zine-ssg.io</generator>
    <language>en-US</language>
    <lastBuildDate>Wed, 22 Apr 2026 09:00:52 +0000</lastBuildDate>
    
      <item>
        
        
          <title>Addressing the harassment</title>
          <description>
            &lt;blockquote&gt;&lt;p&gt;Kiwi Farms is a web forum that facilitates the discussion and harassment of online figures and communities. Their targets are often subject to organized group trolling and stalking, as well as doxing and real-life harassment. Kiwi Farms has been tied to the suicides of three people who were victims of harassment by the website.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;– &lt;a href=&quot;https://en.wikipedia.org/w/index.php?title=Kiwi_Farms&amp;oldid=1348820184&quot; target=&quot;_blank&quot;&gt;Wikipedia: Kiwi Farms&lt;/a&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;About three years ago, a thread on Kiwi Farms was opened about me. In the years since, it has grown to about 1,200 posts full of bigots responding to anything and everything I do online with scorn, slurs, and overt bigotry. The thread is full of resources to facilitate harassment, including, among other things, all of my social media profiles, past and present, a history of my residential addresses, my phone numbers, details about my family members, a list of my usernames and password hashes from every leaked database of websites I have accounts on, and so on. Most of my articles or social media posts are archived on Kiwi Farms and then subjected to the most bigoted rebuttals you can imagine. Honestly, it’s mostly just… pathetic. But it’s a problem when it escapes containment, and it’s designed to.&lt;/p&gt;&lt;p&gt;Kiwi Farms is the most organized corner of the harassment which comes my way, but it comes in many forms. On Mastodon, for example, before I deleted my account I would often receive death threats, or graphic images and videos of violence against minorities. I have received a lot of hate and death threats over email, too, several of which I confess that I took some pleasure in forwarding to the sender’s employer.&lt;/p&gt;&lt;p&gt;One of the motivations for this harassment is to “milk” me for “drama”. The idea is to get my hackles up, make me fearful for my safety, and alienate me from my communities, with the hope that it will trigger an entertaining meltdown. Maybe people respond poorly to this kind of harassment – that’s the idea, really – and it often makes the situation worse. Responding to it can legitimize the abuse, elevate it into the discourse, draw more attention to it, and stoke the flames. It can make the victim look bad when they respond emotionally to harassment designed to evoke negative emotions. I have left it unaddressed for a long time in order to subvert this goal, and address it now with a cool head in a relatively quiet period in the harassment campaign.&lt;/p&gt;&lt;p&gt;The harassment waxes and wanes over time, usually picking up whenever I write a progressive blog post that gets some reach. It really took off after a series of incidents in which I called for the Hyprland community and its maintainers to be held to account for the bigotry and harassment on their Discord server (&lt;a href=&quot;https://drewdevault.com/blog/Hyprland-toxicity/&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://drewdevault.com/blog/FDO-conduct-enforcement/&quot;&gt;2&lt;/a&gt;) and when I spoke out against Richard Stallman’s prolific and problematic public statements regarding the sexual abuse of minors (&lt;a href=&quot;https://drewdevault.com/blog/RMS-on-sex/&quot;&gt;3&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;The abuse crescendoed in October of 2024, when I was involved in editing &lt;a href=&quot;https://stallman-report.org/&quot; target=&quot;_blank&quot;&gt;The Stallman Report&lt;/a&gt;. The report is a comprehensive analysis of Richard Stallman’s problematic political discourse regarding sexual harassment, sexual assault, and the sexual abuse of minors, and it depends almost entirely on primary sources – quotes from Stallman’s website which remain online and have not been retracted to this day. The purpose of the report was to make a clear and unassailable case for Stallman’s removal from positions of power, make specific recommendations to address the underlying problems, and to stimulate a period of reflection and reform in the FOSS community. It didn’t achieve much, in the end: the retaliation from Stallman’s defenders was fiercer and more devoted than the support from those who saw the report’s sense.&lt;/p&gt;&lt;p&gt;Myself and the other authors asserted our &lt;a href=&quot;https://en.wikipedia.org/wiki/Moral_rights&quot; target=&quot;_blank&quot;&gt;moral rights&lt;/a&gt; to publish anonymously, motivated by our wish to reduce our exposure to the exact sort of harassment I’ve been subjected to over the years. However, I was careless in my opsec during the editing process, and it was possible to plausibly link me to the report as a result, leading to a sharp increase in harassment.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;This brings me to a retaliatory, defamatory “report” published about me in the style of the Stallman Report.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This report is, essentially, a distillation of the Kiwi Farms thread on me, sanitized of overt bigotry and presented in a readily linkable form in order to stalk me around the internet and enable harassment. It’s used to discredit anything I do online and push for my exclusion from online communities, by dropping the link on Hacker News, Reddit, GitHub or Codeberg issues, etc, anywhere myself or my work is mentioned, or used to discredit the Stallman Report by discrediting one of its unmasked authors.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The report is pretty obviously written in bad faith and relies on a lot of poor arguments to make the case that I’m a misogynist and a pedophile, charges I deny. It also accuses me of being a hypocrite, which I acknowledge in general terms, because, well, who isn’t. The key thing I want people who encounter this report to keep in mind is that this is the “polite” face of an organized harassment campaign.&lt;/p&gt;&lt;p&gt;Most reasonable readers easily dismiss the report because it is rather transparent in its bad faith. However, someone who reads it in good faith, just trying to do their due diligence, might come away from it with some reasonable concerns. Consider the following quote from my long-deleted Reddit account, /u/sircmpwn:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I’m of the opinion that 14 year old girls should be required to have an IUD installed. Ten years of contraception that requires a visit to the doctor to remove prematurely.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This comment was written 13 years ago, and I don’t stand by what I wrote. I was 19 at the time, and I was a moron. My mother had me when she was 23 years old, and the abuse I suffered at her hands during my childhood was severe, and I generalized this experience to all women. When I wrote this comment, I was one year removed from the abuse, living alone and in poverty, and early in a life-long process of coming to terms with the abuse and figuring out how to be a well-adjusted adult after 18 long years of abuse and isolation.&lt;/p&gt;&lt;p&gt;But an explanation is not an excuse. This comment was reprehensible, as were many of the awful ideas I held at the time. Many years later, I can recognize that this comment is misogynistic, denies the agency of children and women over their own bodies, disparages the many, many mothers who do a wonderful job raising children in difficult circumstances, and is based in argumentation which can reasonably be related to eugenics. This comment was just awful – there’s a reason this was deleted. I apologize to anyone who read it at the time, or comes across it now, and is justifiably insulted.&lt;/p&gt;&lt;p&gt;I don’t feel that it’s necessary to rebuke most of the report. But, there is a grain of truth in the report, the grain of truth that led me to retract my shitty Reddit comments and reflect on myself, and that grain of truth is this: in early adulthood, I was a huge asshole.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;I have had more than my fair share of harmful ignorance, bad takes, sexism and misogyny, transphobic and homophobic beliefs, and worse. Moreover, I have verbally abused many people and made many of my own arguments in bad faith to support bad conclusions. Some of the people who read this will recall having found themselves at the wrong end of my verbal abuse and harassment.&lt;/p&gt;&lt;p&gt;It’s important for me to take responsibility for this period of my life, and in dismissing bad faith criticisms of myself to carefully avoid dismissing good faith criticisms in the same fell swoop.&lt;/p&gt;&lt;p&gt;I’m not really sure how to deal with this part of my life appropriately.  I have apologized to a few people individually, but it’s not a scalable solution and with many people I have no business re-opening wounds to salve my own conscience. I can offer a general apology, and I will. I’ve never found the right moment to say it, but now will do: I apologise, sincerely, to everyone who I have harmed with verbal abuse and with hateful and problematic rhetoric. If you have had a bad experience or experiences with me, and there’s anything you want from me that can help you heal from that experience – a personal apology, for example – please reach out to me and ask.&lt;/p&gt;&lt;p&gt;That said, apologies alone aren’t enough. I believe in restorative justice, in growing and mending wounds and repairing harm done, and I set myself seriously to this task over many years. I have gone to therapy, spoken with close friends about it, and taken structural action as well: I have founded support groups and worked one-on-one with many of the people whose politics and behavior I object to. I want an amicable end to bigotry and bullying, for bigots and bullies like my former self to look forward to, to provide a path that doesn’t require them to double down. It’s not easy, and not everyone manages, but I have to look at myself and see the path I’ve taken and imagine that it’s possible, because what’s left for the likes of me if not?&lt;/p&gt;&lt;p&gt;This part of my past brings me a great deal of shame, and that shame motivates me to grow as a person. In a certain sense, it is an ironic, cruel privilege to have had so much cause to reflect on myself, to drive me to question myself and my ideas, and become a much better person with much more defensible ideas. It has driven me to study feminism, social justice, racial justice, intersectionality, LGBTQ theory, antifascism, and to find the intersections in my own life and strive to act out of a more legitimate sense of justice.&lt;/p&gt;&lt;p&gt;I’m often still a firebrand, but I’ve chosen much better hills to die on. My passion is invested in making a more just world, building safe and healthy communities, elevating my peers, and calling for justice and a just society. I have taken the lessons I have learned and tried to share them with other people, and to stand up for what I can now say I &lt;em&gt;know&lt;/em&gt; is right, both online and in real life. Through a process of learning, reflection, and humility, I acknowledge that I have done a lot harm in my youth. To repair this harm, I have committed myself to doing more than enough good now to make sure that the world is a better place when all is said and done. That’s what justice means to me when I turn my principles inwards and hold myself accountable.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;So where do we go from here?&lt;/p&gt;&lt;p&gt;The response to my progressive beliefs and activism is reactionary backlash, doxing, harassment, and death threats targeting me and my family, all of which is likely to escalate in response to this post, and none of which is defensible. On the other hand, I understand that the consequences for my own reactionary past is, in some cases, alienation – and, honestly, fair enough.&lt;/p&gt;&lt;p&gt;But I don’t want you to confuse my honest faults with the defamation and harassment I endure for standing up for my honest strengths. If you feel generous and optimistic about who I am today, and you recognize my growth, and wish for an ally in the fight for what’s right, your good faith and solidarity mean the world to me. I would appreciate it if you would express your support and rebuke harassment when you see it, and help keep me honest as I continue a life-long process of learning and growth.&lt;/p&gt;&lt;p&gt;If I’ve hurt you, and you want to seek reconciliation, I make myself available to you for that purpose. If I’ve hurt you, and you simply don’t care to be hurt again, I’m sorry – I understand where you’re coming from, and have made my peace with it.&lt;/p&gt;&lt;p&gt;Please send words of support and/or death threats to &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;drew@ddevault.org&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Thank you.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Addressing-harassment/</link>
        
        <pubDate>Tue, 21 Apr 2026 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Addressing-harassment/</guid>
      </item>
    
      <item>
        
        
          <title>Rewrote my blog with Zine</title>
          <description>
            &lt;p&gt;15 years ago, on December 11th, 2010, at the bold age of 17, I wrote &lt;a href=&quot;https://web.archive.org/web/20120305160351/http://sircmpwn.blogspot.com/2010/12/everything-you-want-out-of-wp7.html&quot; target=&quot;_blank&quot;&gt;my first blog post&lt;/a&gt; on the wonders of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_Phone_7&quot; target=&quot;_blank&quot;&gt;Windows Phone 7&lt;/a&gt; on Blogspot. I started blogging as a kid at the behest of a family friend at Microsoft, who promised she’d make sure I would become the youngest &lt;a href=&quot;https://en.wikipedia.org/wiki/Microsoft_Most_Valuable_Professional&quot; target=&quot;_blank&quot;&gt;Microsoft MVP&lt;/a&gt; if I started blogging. That never came to pass, though, because as I entered adulthood and started to grow independent of my Microsoft-friendly family I quickly began down the path to the free and open source software community.&lt;/p&gt;&lt;p&gt;Early blog posts covered intriguing topics such as complaining about my parent’s internet filter, a horrible hack to “replace” the battery of a dead gameboy game, announcing my friend’s Minecraft guild had a new website (in PHP), and so on. After Blogspot, I moved to &lt;a href=&quot;https://jekyllrb.com/&quot; target=&quot;_blank&quot;&gt;Jekyll&lt;/a&gt; on GitHub pages, publishing &lt;a href=&quot;https://drewdevault.com/blog/You-dont-need-jQuery/&quot;&gt;You don’t need jQuery&lt;/a&gt; in 2013. For a long time this was the oldest post on the site.&lt;/p&gt;&lt;p&gt;I’m pretty proud of my writing skills and have a solid grasp on who I am today, but the further back you go the worse my writing, ideas, values, and politics all get. I was growing up in front of the world on this blog, you know? It’s pretty embarassing to keep all of this old stuff around. But, I decided a long time ago to keep all of it up, so that people can understand where I’ve come from, and that everyone has to start somewhere.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;At some point – I’m not sure when – I switched from Jekyll to &lt;a href=&quot;https://gohugo.io/&quot; target=&quot;_blank&quot;&gt;Hugo&lt;/a&gt;, and I’ve stuck with it since. But lately I’ve been frustrated with it. I’d like my blog engine to remain relatively stable and simple, but Hugo is quite complex and over the past few years I’ve been bitten by a number of annoying and backwards-incompatible changes. And, as part of my efforts to remove vibe-coded software from my stack, I was disappointed to learn that &lt;a href=&quot;https://github.com/gohugoio/hugo/blob/842d8f105256c5656e7895ee61fa5b2dfe90a9e3/AGENTS.md&quot; target=&quot;_blank&quot;&gt;Hugo is being vibe coded now&lt;/a&gt;, and so rewriting my blog went onto the todo list.&lt;/p&gt;&lt;p&gt;Choosing the right static site generator (SSG) was a bit of a frustrating process. Other leading candidates, like Pelican or Zola, are also built from slop now. But a few months ago I found &lt;a href=&quot;https://zine-ssg.io/&quot; target=&quot;_blank&quot;&gt;Zine&lt;/a&gt;, and after further study I found it to be a pretty promising approach. Over the past few days I have rewritten my templates and ported in nearly 400 (jeesh) blog posts from my archives.&lt;/p&gt;&lt;p&gt;There’s a lot to like about Zine. I’m pretty intrigued by SuperHTML as a templating engine design – the templates are all valid HTML5 and use an interesting approach to conditions, loops, and interpolation. SuperMD has some interesting ideas, but I’m less sold on it. The Scripty language used for interpolation and logic is a bit iffy in terms of design – feels half baked. And the designers had some fun ideas, like &lt;a href=&quot;https://zine-ssg.io/log/&quot; target=&quot;_blank&quot;&gt;devlogs&lt;/a&gt;, which I feel are kind of interesting but tend to have an outsized influence on the design, more polished where the polish might have been better spent elsewhere. The development web server tends to hang fairly often and I’ve gotten it to crash with esoteric error messages every now and then.&lt;/p&gt;&lt;p&gt;But what can I say, it’s alpha software – I hope it will improve, and I’m betting that it will by migrating my blog. There’s no official LLM policy (&lt;a href=&quot;https://github.com/kristoff-it/zine/issues/200&quot; target=&quot;_blank&quot;&gt;yet&lt;/a&gt;) and I hope they will end up &lt;a href=&quot;https://github.com/kristoff-it/zine/issues/199&quot; target=&quot;_blank&quot;&gt;migrating to Codeberg&lt;/a&gt;, and using Discord for project communication is &lt;a href=&quot;https://drewdevault.com/blog/Dont-use-Discord-for-FOSS/&quot;&gt;not something I appreciate&lt;/a&gt;, but maybe they’ll change their tune eventually.&lt;/p&gt;&lt;p&gt;In the meantime, I took the opportunity to clean up the code a bit. The canonical links have gone through several rounds of convention and backwards compatibility, and I have replaced them with a consistent theme and set up redirects. I probably broke everyone’s feed readers when rolling these changes out, and I apologise for that. I have gone through the backlog and updated a number of posts as best as I can to account for bitrot, but there are still a lot of broken videos and links when you get far enough back – hopefully I can restore some of that given enough time.&lt;/p&gt;&lt;p&gt;I’ve also gone ahead and imported the &lt;em&gt;really old&lt;/em&gt; stuff from Blogspot. The whole lot is garbage, but if you’re curious to see where I started out, these old posts are more accessible now.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Rewrite-with-zine/</link>
        
        <pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Rewrite-with-zine/</guid>
      </item>
    
      <item>
        
          <title>🚀 Signal boost: The Future of Everything is Lies, I Guess</title>
          <description>
            <p>🚀 Signal boost: someone else's article that I think you should read</p>
            <p>
              <a href="https://aphyr.com/posts/411-the-future-of-everything-is-lies-i-guess">The Future of Everything is Lies, I Guess</a>
            </p>
          </description>
          <link>https://drewdevault.com/blog/Future-of-everything-is-lies/</link>
        
        
        <pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Future-of-everything-is-lies/</guid>
      </item>
    
      <item>
        
        
          <title>tar: a slop-free alternative to rsync</title>
          <description>
            &lt;p&gt;So apparently &lt;a href=&quot;https://codeberg.org/small-hack/open-slopware&quot; target=&quot;_blank&quot;&gt;rsync is slop&lt;/a&gt; now. When I heard, I wanted to drop a quick note on my blog to give an alternative: tar. It doesn’t do everything that rsync does, in particular identifying and skipping up-to-date files, but tar + ssh can definitely accomodate the use case of “transmit all of these files over an SSH connection to another host”.&lt;/p&gt;&lt;p&gt;Consider the following:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;tar -cz public | ssh example.org tar -C /var/www -xz
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will transfer the contents of &lt;code&gt;./public/&lt;/code&gt; to &lt;code&gt;example.org:/var/www/public/&lt;/code&gt;, preserving file ownership and permissions and so on, with gzip compression. This is roughly the equivalent of:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;rsync -a public example.org:/var/www/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here’s the same thing with a lightweight progress display thanks to &lt;a href=&quot;https://ivarch.com/programs/pv.shtml&quot; target=&quot;_blank&quot;&gt;pv&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;tar -cz public | pv | ssh example.org tar -C /var/www -xz
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I know tar is infamously difficult to remember how to use. Honestly, I kind of feel that way about rsync, too. But, here’s a refresher on the most important options for this use-case. To use tar, pick one of the following modes with the command line flags:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;-c&lt;/code&gt;: create an archive&lt;/li&gt;&lt;li&gt;&lt;code&gt;-x&lt;/code&gt;: extract an archive&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Use &lt;code&gt;-f &amp;lt;filename&amp;gt;&lt;/code&gt; to read from or write to a file. Without this option, tar uses stdin and stdout, which is what the pipelines above rely on. Use &lt;code&gt;-C &amp;lt;path&amp;gt;&lt;/code&gt; to change directories before archiving or extracting files. Use &lt;code&gt;-z&lt;/code&gt; to compress or decompress the tarball with gzip. That’s basically everything you need to know about tar to use it for this purpose (and for most purposes, really).&lt;/p&gt;&lt;p&gt;With rsync, to control where the files end up you have to memorize some rules about things like whether or not each path has a trailing slash. With tar, the rules are, in my opinion, a bit easier to reason about. The paths which appear on the command line of &lt;code&gt;tar -c&lt;/code&gt; are the paths that &lt;code&gt;tar -x&lt;/code&gt; will open to create those files. So if you run this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;tar -c public/index.html public/index.css
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You get a tarball which has &lt;code&gt;public/index.html&lt;/code&gt; and &lt;code&gt;public/index.css&lt;/code&gt; in it.&lt;/p&gt;&lt;p&gt;When &lt;code&gt;tar -x&lt;/code&gt; opens this tarball, it will call &lt;code&gt;fopen(&amp;quot;public/index.html&amp;quot;, &amp;quot;w&amp;quot;)&lt;/code&gt;. So, whatever tar’s working directory is, it will extract this file into &lt;code&gt;./public/index.html&lt;/code&gt;. You can change the working directory before tar does this, on either end, by passing &lt;code&gt;tar -C &amp;lt;path&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Of course, you could just use scp, but this fits into my brain better.&lt;/p&gt;&lt;p&gt;I hope that’s useful to you!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: As a fun little challenge I wrapped up this concept in a small program that makes it easier to use:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/xtar&quot; target=&quot;_blank&quot;&gt;https://git.sr.ht/~sircmpwn/xtar&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Example:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;xtar -R /var/www me@example.org public/*
&lt;/code&gt;&lt;/pre&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/rsync-without-rsync/</link>
        
        <pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/rsync-without-rsync/</guid>
      </item>
    
      <item>
        
        
          <title>A eulogy for Vim</title>
          <description>
            &lt;p&gt;Vim is important to me. I’m using it to write the words you’re reading right now. In fact, almost every word I have ever committed to posterity, through this blog, in my code, all of the docs I’ve written, emails I’ve sent, and more, almost all of it has passed through Vim.&lt;/p&gt;&lt;p&gt;My relationship with the software is intimate, almost as if it were an extra limb. I don’t think about what I’m doing when I use it. All of Vim’s modes and keybindings are deeply ingrained in my muscle memory. Using it just feels like my thoughts flowing from my head, into my fingers, into a Vim-shaped extension of my body, and out into the world. The unique and profound nature of my relationship with this software is not lost on me.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/66af7169.jpg&quot;&gt;
&lt;figcaption&gt;A picture of my right hand, with the letters “hjkl” tattooed on the wrist&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I didn’t know Bram Moolenaar. We never met, nor exchanged correspondence. But, after I moved to the Netherlands, Bram’s home country, in a strange way I felt a little bit closer to him. He passed away a couple of years after I moved here, and his funeral was held not far from where I lived at the time. When that happened, I experienced an odd kind of mourning. He was still young, and he had affected my own life profoundly. He was a stranger, and I never got to thank him.&lt;/p&gt;&lt;p&gt;The people he entrusted Vim to were not strangers, they knew Bram and worked with him often, and he trusted them. It’s not my place to judge their work as disrespectful to his memory, or out of line with what he would have wanted. Even knowing Bram only through Vim, I know he and I disagreed often. However, the most personal thing I know about Bram, and that many people remember about him, was his altruistic commitment to a single cause: providing education and healthcare to Ugandan children in need. So, at the very least, I know that he cared.&lt;/p&gt;&lt;p&gt;I won’t speculate on how he would have felt about generative AI, but I can say that GenAI is something I care about. It causes a lot of problems for a lot of people. It drives rising energy prices in poor communities, disrupts wildlife and fresh water supplies, increases pollution, and stresses global supply chains. It re-enforces the horrible, dangerous working conditions that miners in many African countries are enduring to supply rare metals like Cobalt for the billions of new chips that this boom demands. And at a moment when the climate demands immediate action to reduce our footprint on this planet, the AI boom is driving data centers to consume a full 1.5% of the world’s total energy production in order to eliminate jobs and replace them with a robot that lies.&lt;/p&gt;&lt;p&gt;Meanwhile, this whole circus is enabling the rising tide of fascism around the world, not only by supercharging propaganda but also by directly financially supporting fascist policies and policymakers.  All this to enrich the few, centralize power, reduce competition, and underwrite an enormous bubble that, once it bursts, will ruin the lives of millions of the world’s poor and marginalized classes.&lt;/p&gt;&lt;p&gt;I don’t think it’s &lt;em&gt;cute&lt;/em&gt; that someone &lt;a href=&quot;https://www.vim.org/vim-9.2-released.php#:~:text=The%20maturity,Copilot&quot; target=&quot;_blank&quot;&gt;vibe coded “battleship” in VimScript&lt;/a&gt;. I think it’s more important that we stop collectively pretending that we don’t understand how awful all of this is. I don’t want to use software which has slop in it. I do what I can to avoid it, and sadly even Vim now comes under scrutiny in that effort as both &lt;a href=&quot;https://github.com/vim/vim/issues/18800#issuecomment-3568099543&quot; target=&quot;_blank&quot;&gt;Vim&lt;/a&gt; and &lt;a href=&quot;https://github.com/neovim/neovim/pulls?q=label%3A%22AI%20assisted%20%F0%9F%A4%96%22&quot; target=&quot;_blank&quot;&gt;NeoVim&lt;/a&gt; are relying on LLMs to develop the software.&lt;/p&gt;&lt;p&gt;So this is how, a few years after Bram’s passing, I find myself in another unusual moment of mourning: mourning Vim itself. What an odd feeling.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;To keep my conscience clear, and continue to enjoy the relationship I have with this amazing piece of software, I have forked Vim. You can find my fork here: &lt;a href=&quot;https://sr.ht/~sircmpwn/vim-classic/&quot; target=&quot;_blank&quot;&gt;Vim Classic&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The choice of which version to use as the basis for a fork was a bit difficult. The last version of Vim released during Bram’s lifetime was Vim 9.0. To me, that seems like a good starting point. But, in the end, I chose to base my fork on Vim 8.2.0148 instead. Patch 148 was the patch immediately prior to the introduction of Vim9 Script, Vim 9.0’s flagship feature.&lt;/p&gt;&lt;p&gt;I’m sure Bram worked hard on Vim9 script, and I want to honor that. At the same time, it was still very new when he passed away, and the job of fully realizing its potential was handed down to the current maintainers. Its absence from Vim Classic is an honest assessment that I don’t have the time or energy to try to sort out all of the work on Vim9 which followed in Bram’s footsteps, and decide what stays and what goes. It seems like a useful line to draw in the sand: Vim Classic is compatible with legacy plugins, but not the newfangled stuff.&lt;/p&gt;&lt;p&gt;Since forking from this base, I have backported a handful of patches, most of which address CVEs discovered after this release, but others which address minor bug fixes. I also penned a handful of original patches which bring the codebase from this time up to snuff for building it on newer toolchains. My old vimrc &lt;a href=&quot;https://paste.sr.ht/~sircmpwn/bf58300e4b11542b17eeece9a3671656172f86c8&quot; target=&quot;_blank&quot;&gt;needed very few changes&lt;/a&gt; to work on this version of Vim, and all of my plugins work &lt;del&gt;with the exception of fzf.vim, which I would like to fix at some point (or maybe a sympathetic reader is willing to work on backporting the necessary changes)&lt;/del&gt;. &lt;em&gt;Thanks, dzwdz, for figuring out the issues with fzf.vim!&lt;/em&gt;&lt;/p&gt;&lt;p&gt;I plan to use this for a little while, look for sore points and rough edges, collect feedback from other users, and then tag a little release soon. Going forward, maintenance will be slow and quiet. I welcome your patches, particularly to help with maintaining the runtime scripts, stuff like making sure new language features end up in the syntax files. I’ll also gladly accept new bug fixes, and maybe even a few new features if a good case can be made for including them. Backporting small patches from Vim upstream will be considered, with extra scrutiny.&lt;/p&gt;&lt;p&gt;In short, I invite you to use Vim Classic, if you feel the same way as me, and to maintain it with me, contributing the patches you need to support your own use cases.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Forking-vim/</link>
        
        <pubDate>Wed, 25 Mar 2026 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Forking-vim/</guid>
      </item>
    
      <item>
        
        
          <title>The cults of TDD and GenAI</title>
          <description>
            &lt;p&gt;I’ve gotten a lot of flack throughout my career over my disdain towards test-driven development (TDD). I have met a lot of people who swear by it! And, I have also met a lot of people who insisted that I adopt it, too, often with the implied threat of appealing to my boss if appealing to me didn’t work.&lt;/p&gt;&lt;p&gt;The basic premise of TDD, for those unaware, is that one &lt;em&gt;first&lt;/em&gt; writes a unit test that verifies the expected behavior for some code they want to write, observes the new test fail, and &lt;em&gt;then&lt;/em&gt; one writes the implementation, iterating on it until the test passes. The advantage of this approach is, first, to ensure that your codebase is adequately covered by testing, and, second, to provide you a rapid feedback loop to assist in your work.&lt;/p&gt;&lt;p&gt;I have often found elements of TDD to be quite useful. Using a unit test or something similar to provide an efficient rapid feedback loop is a technique which I have employed many times. However, I am and have always been skeptical of the cult which arises around automated software testing and in particular TDD. A lot of people adopt an unquestioning loyalty to TDD, building tools and practices and &lt;span class=&quot;rainbow&quot;&gt;vibes&lt;/span&gt; around the idea. It’s often too much.&lt;/p&gt;&lt;style&gt;
.rainbow {
  font-weight: bold;
  background-image: linear-gradient(to left, violet, indigo, blue, green, yellow, orange, red);
  background-clip: text;
  background-size: 800% 800%;
  animation: rainbow 8s ease infinite;
  -webkit-text-fill-color: transparent;
}

@keyframes rainbow { 
    0%{background-position:0% 50%}
    50%{background-position:100% 25%}
    100%{background-position:0% 50%}
}
&lt;/style&gt;
&lt;p&gt;The flaw with TDD is that, while it ensures that you have a test for every function you write, it also exerts an influence on the tested codebase, shaping the code to be as “testable” as possible, which only sometimes leads to better code. Moreover, TDD has no means of ensuring that the behavior that your tests verify is the &lt;em&gt;right&lt;/em&gt; behavior for your software to have. Software with a thousand passing tests and 100% test coverage could be doing whatever the user or the business or whatever needs it to, but it could just as easily not meet the requirements in spite of those comprehensive tests – and in any case it gives you confidence in your work, which may or may not be misplaced.&lt;/p&gt;&lt;p&gt;The cult of TDD exploits the fact that TDD is very good at making you feel like a good, diligent programmer. That rapid feedback loop not only assists in your work but also enables a powerful dopamine cycle. Add into that a culture of aiming for 100% coverage and you get the bonus hit from watching a number go up. Buy into the whole cult and you get a slew of new README badges to keep green, and lots of cool charts and numbers, hundreds of blinkenlights on your test suite, a bunch of fun Slack messages from Jenkins, and a cute cardboard cut-out of the CTO to keep in the cubicle of whoever last broke the build.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; All of this pomp and circumstance is &lt;em&gt;fun&lt;/em&gt; and it feels &lt;em&gt;good&lt;/em&gt; and because it’s all in the name of testing (which is good, right?) it makes you feel like a &lt;em&gt;good programmer&lt;/em&gt; even if none of it necessarily contributes to the results your team is supposed to achieve.&lt;/p&gt;&lt;p&gt;All of these flashy traits allow one to adopt the aesthetics of good, diligent software engineering work regardless of how good the work actually is. It’s an intoxicating way to work, especially for someone who struggles with software engineering. It makes you feel like a good programmer and gives you data to “back it up”, stuff you can cite at your performance review. But, software development is really hard, and TDD doesn’t go that far to making it easier. All of the really hard problems are not solved by TDD.&lt;/p&gt;&lt;p&gt;I suspect that coding agents are tapping into the same emotional and psychological reflexes that the cult of TDD gives us an early example of. Software development is still hard, but using an agent allows someone who’s just “so-so” at programming to feel the rush of being great at programming, a rush they might have been chasing for their entire career, and I bet the rush is so much sweeter than watching the lights on your test suite runs tick over to green.&lt;/p&gt;&lt;p&gt;A coding agent permits one to feel as if they have the raw productive power a great programmer can tap into. One may feel like the “10× programmers” they’ve sat next to in the open office for ten years, whose skills they never quite achieved themselves. It scales up the raw output by a factor of ten, and lets one assemble apparently great works in a fraction of the time, solo-coding great cathedrals in the time it used to take them to build, with great difficulty, a homely shack.&lt;/p&gt;&lt;p&gt;But, if it seems too good to be true…&lt;/p&gt;&lt;p&gt;Those cathedrals are not the great works they appear to be. The construction is shoddy and the architecture nonsensical and a great programmer hand-writing code will still outperform any mediocre programmer once the gleam wears off of their respective works and the bugs and problems start showing up. The project has 99.9% coverage on a thousand beautiful green tests, and, inside, the foundations are still rotten.&lt;/p&gt;&lt;p&gt;God, though, I understand why so many people are chasing that dragon, even though it’s going to ruin their careers, and maybe even their lives. I get why people fall for this, in spite of the externalities that they must know of by now. In spite of the colossal waste, the loss of fresh water resources, the fact that AI datacenters are the fastest growing source of carbon emissions, the people suffering sky-rocketing power bill and rolling outages near these new datacenters, the reams and reams of fascist propaganda these machines are producing to tear our society apart, the corruption, the market manipulation, the plain and simple fact that the ultimate purpose of these tools is to put their users &lt;em&gt;out of a job entirely&lt;/em&gt;… well, once you finally get a taste of what it feels like to be &lt;em&gt;great&lt;/em&gt;… I suppose all of those problems seem so far away.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Cult-of-TDD-and-LLMs/</link>
        
        <pubDate>Thu, 29 Jan 2026 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Cult-of-TDD-and-LLMs/</guid>
      </item>
    
      <item>
        
        
          <title>Redesigning my microkernel from the ground up</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://drewdevault.com/blog/helios/&quot;&gt;As you may recall&lt;/a&gt;, circa 2022-2023 I was working on a microkernel written in Hare named Helios. Helios was largely inspired by and modelled after the design of &lt;a href=&quot;https://sel4.systems/&quot; target=&quot;_blank&quot;&gt;seL4&lt;/a&gt; and was my first major foray into modern OS development that was serious enough to get to a somewhat useful state of functionality, with drives for some real hardware, filesystems, and an environment for running user programs of a reasonable level of sophistication.&lt;/p&gt;&lt;p&gt;Helios development went strong for a while but eventually it slowed and eventually halted in a state of design hell. Since Helios was my first major OS project at this scale and with this much ambition, the design and implementation ended up with a lot of poor assumptions that made it a pretty weak foundation for building a complete OS upon. In late 2023 I more or less gave up on it and moved my OS development work out of the realm of writing code and back into the realm of thinking really hard about how to design operating systems.&lt;/p&gt;&lt;p&gt;What followed was a couple of years of design thinking, developing small scale design experiments, and doing deeper research into prior art – reading papers and studying existing kernels. It was also during this period that I wrote &lt;a href=&quot;https://drewdevault.com/blog/Bunnix/&quot;&gt;Bunnix&lt;/a&gt;, a working Unix clone, motivated in part by a desire to gain some first-hand experience working in the design and implementation of Unix-style operating systems – a fertile environment for learning a lot of the nuts and bolts of OS implementations by working against a complete and proven design.&lt;/p&gt;&lt;p&gt;In August I was finally prepared to have another go. I decided to start over from scratch, importing and adapting and rewriting code from Helios and Bunnix on an as-needed basis to speed things up, and writing from scratch anything where the lessons learned in hindsight outweighed the benefits of adapting existing code.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The result is &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hermes&quot; target=&quot;_blank&quot;&gt;Hermes&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Hermes has not yet reached feature parity with Helios, lacking some IPC features and an aarch64 port, but already it’s significantly more robust and thoughtfully designed than Helios.&lt;/p&gt;&lt;p&gt;The big glitzy feature that most obviously distinguishes Hermes from Helios is that Hermes supports symmetric multiprocessing (SMP), which is to say, running on multiple CPU cores. This time around, I finally listened to the advice I’d been hearing in osdev circles for years and implemented SMP as early as possible to avoid dealing with tons of problems adding multiprocessing to an otherwise mature kernel.&lt;/p&gt;&lt;p&gt;The multicore scheduler at the heart of Hermes is surprisingly simple, actually. It uses relatively ordinary per-CPU run queues. Each new task, once scheduleable, is scheduled, in order of preference, on (1) the CPU matching its affinity, (2) any currently idle CPU, or (3) a random CPU. If a CPU would idle, it first tries to &lt;a href=&quot;https://en.wikipedia.org/wiki/Work_stealing&quot; target=&quot;_blank&quot;&gt;steal&lt;/a&gt; a pending task from another CPU. The most important parts of the scheduler are less than 200 lines of code (&lt;a href=&quot;https://git.sr.ht/~sircmpwn/hermes/tree/main/item/sched/sched.ha&quot; target=&quot;_blank&quot;&gt;[1]&lt;/a&gt;, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hermes/tree/main/item/sched/runq.ha&quot; target=&quot;_blank&quot;&gt;[2]&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;The less obviously impressive improvements from Helios to Hermes are numerous. The syscall and IPC ABIs were rethought from the ground up – one of the major goals of the redesign. I also moved from an seL4-style capability derivation graph – which is quite complex to implement and reason about – to reference counting to manage the lifetimes of kernel resources. Resource management in general is much simpler and should improve the performance of the kernel substantially.&lt;/p&gt;&lt;p&gt;I’ve also taken a much different approach to organizing the code, to allow the kernel and many of the things around it – its bootloaders and the userspace that runs the kernel test suite – share a lot more code than was possible in Helios, making a lot of the non-kernel code a lot easier to write and maintain.&lt;/p&gt;&lt;p&gt;The userspace is also a substantial upgrade in design from Helios, or at least I hope it will be when more of it takes shape. Rather than developing a specialized Hare standard library, independent of the upstream Hare standard library, for writing drivers and low-level services, I have started with a port of the upstream Hare standard library and built low-level driver and service support libraries around it. The userspace is streamlined considerably by doing so, giving these low-level components access to a more comfortable and featureful programming environment and reducing the complexity of the system by making various components more uniform in their design.&lt;/p&gt;&lt;p&gt;Finally, I’ve taken a much more serious approach to testing Hermes and making it as robust and complete as possible in real-world use-cases. I borrowed the EFI bootloader from Bunnix and repurposed it for Hermes, opening up a lot of newer hardware, and I have written a more comprehensive test suite and run and verified it on much more real-world hardware. I have about ten devices which all (consistently!) pass the Hermes test suite. Feel free to &lt;a href=&quot;https://redacted.moe/dl/69a8581c.iso&quot; target=&quot;_blank&quot;&gt;try it out on yours&lt;/a&gt; as well and let me know how it goes!&lt;/p&gt;&lt;p&gt;That’s all there is to say for now, but I hope to keep you in the loop as I continue working on this for a while. The userspace is starting to take shape and soon(™) I hope to start building out block device drivers, some filesystems, and enough support code to run a shell and a handful of useful programs. In the meantime, feel free to poke around the code and play around with it. There is also some &lt;a href=&quot;https://hermes.ares-os.org/&quot; target=&quot;_blank&quot;&gt;early documentation&lt;/a&gt; available for you to read if you wish. I’m hanging out in #ares on Libera Chat if you have any questions.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Hermes-from-the-ground-up/</link>
        
        <pubDate>Mon, 12 Jan 2026 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Hermes-from-the-ground-up/</guid>
      </item>
    
      <item>
        
        
          <title>OpenAI employees… are you okay?</title>
          <description>
            &lt;p&gt;You might have seen &lt;a href=&quot;https://edition.cnn.com/2025/11/06/us/openai-chatgpt-suicide-lawsuit-invs-vis&quot; target=&quot;_blank&quot;&gt;an article&lt;/a&gt; making the rounds this week, about a young man who ended his life after ChatGPT encouraged him to do so. The chat logs are really upsetting.&lt;/p&gt;&lt;p&gt;Someone two degrees removed from me took their life a few weeks ago. A close friend related the story to me, about how this person had approached their neighbor one evening to catch up, make small talk, and casually discussed their suicidal ideation at some length. At the end of the conversation, they asked to borrow a rope, and their neighbor agreed without giving the request any critical thought. The neighbor found them the next morning.&lt;/p&gt;&lt;p&gt;I didn’t know the deceased, nor their neighbor, but I’m close friends with someone who knew both. I found their story deeply chilling – ice runs through my veins when I imagine how the neighbor must have felt. I had a similar feeling upon reading this article, wondering how the people behind ChatGPT and tools like it are feeling right now.&lt;/p&gt;&lt;p&gt;Two years ago, someone I knew personally took their life as well. I was not friendly with this person – in fact, we were on very poor terms. I remember at the time, I had called a crisis hotline just to ask an expert for advice on how to break this news to other people in my life, many of whom were also on poor terms with a person whose struggles to cope with their mental health issues caused a lot of harm to others.&lt;/p&gt;&lt;p&gt;None of us had to come to terms with any decisions with the same gravity as what that unfortunate neighbor had to face. None of us were ultimately responsible for this person’s troubles or were the impetus for what happened. Nonetheless, the uncomfortable and confronting feelings I experienced in the wake of that event perhaps give me some basis for empathy and understanding towards the neighbor, or for OpenAI employees, and others who find themselves in similar situations.&lt;/p&gt;&lt;p&gt;If you work on LLMs, well… listen, I’ve made my position as an opponent of this technology clear. I feel that these tools are being developed and deployed recklessly, and I believe tragedy is the inevitable result of that recklessness. If you confide in me, I’m not going to validate your career choice. But maybe that’s not necessarily a bad quality to have in a confidant? I still feel empathy towards you and I recognize your humanity and our need to acknowledge each other as people.&lt;/p&gt;&lt;p&gt;If you feel that I can help, I encourage you to reach out. I will keep our conversation in confidence, and you can reach out anonymously if that makes you feel safer. I’m a good listener and I want to know how you’re doing. &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Email me&lt;/a&gt;.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;If you’re experiencing a crisis, &lt;a href=&quot;https://findahelpline.com/&quot; target=&quot;_blank&quot;&gt;24-hour support is available&lt;/a&gt; from real people who are experts in getting you the help you need. Please consider reaching out. All you need to do is follow the link.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/OpenAI-employees-are-you-okay/</link>
        
        <pubDate>Sat, 08 Nov 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/OpenAI-employees-are-you-okay/</guid>
      </item>
    
      <item>
        
        
          <title>What&apos;s up with FUTO?</title>
          <description>
            &lt;p&gt;Some time ago, I noticed some new organization called &lt;a href=&quot;https://futo.org/&quot; target=&quot;_blank&quot;&gt;FUTO&lt;/a&gt; popping up here and there. I’m always interested in seeing new organizations that fund open source popping up, and seeing as they claim several notable projects on their roster, I explored their website with interest and gratitude. I was first confused, and then annoyed by what I found. Confused, because their website is littered with &lt;a href=&quot;https://futo.org/about/what-is-futo/&quot; target=&quot;_blank&quot;&gt;bizzare manifestos&lt;/a&gt;,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and ultimately annoyed because they were playing fast and loose with the term “open source”, using it to describe commercial source-available software.&lt;/p&gt;&lt;p&gt;FUTO eventually clarified their stance on “open source”, first through &lt;a href=&quot;https://futo.org/open-source-definition/&quot; target=&quot;_blank&quot;&gt;satire&lt;/a&gt; and then &lt;a href=&quot;https://futo.org/about/futo-statement-on-opensource/&quot; target=&quot;_blank&quot;&gt;somewhat more soberly&lt;/a&gt;, perpetuating the &lt;a href=&quot;https://drewdevault.com/2022/09/16/Open-source-matters.html&quot; target=&quot;_blank&quot;&gt;self-serving myth&lt;/a&gt; that &lt;a href=&quot;https://drewdevault.com/2021/01/20/FOSS-is-to-surrender-your-monopoly.html&quot; target=&quot;_blank&quot;&gt;“open source” software can privilege one party over anyone else&lt;/a&gt; and &lt;a href=&quot;https://drewdevault.com/2022/03/01/Open-source-is-defined-by-the-OSD.html&quot; target=&quot;_blank&quot;&gt;still be called open source&lt;/a&gt;. I mentally categorized them as problematic but hoped that their donations or grants for genuinely open source projects would do more good than the harm done by this nonsense.&lt;/p&gt;&lt;p&gt;By now I’ve learned better. &lt;strong&gt;tl;dr&lt;/strong&gt;: FUTO is not being honest about their “grant program”, they don’t have permission to pass off these logos or project names as endorsements, and they collaborate with and promote mask-off, self-proclaimed fascists.&lt;/p&gt;&lt;p&gt;An early sign that something is off with FUTO is in that “sober” explanation of their “disdain for OSI approved licenses”, where they make a point of criticizing the Open Source Initiative for banning Eric S. Raymond (aka ESR) from their mailing lists, citing right-wing reactionary conspiracy theorist Bryan Lunduke’s blog post on the incident. Raymond is, as you may know, one of the founders of OSI and a bigoted asshole. He was banned from the mailing lists, not because he’s a bigoted asshole, but because &lt;a href=&quot;https://lists.opensource.org/pipermail/license-discuss_lists.opensource.org/2020-February/021328.html&quot; target=&quot;_blank&quot;&gt;he was being a toxic jerk&lt;/a&gt; &lt;em&gt;on the mailing list in question&lt;/em&gt;. Healthy institutions outgrow their founders. That said, FUTO’s citation and perspective on the ESR incident could be generously explained as a simple mistake, and we should probably match generosity with generosity given their prolific portfolio of open source grants.&lt;/p&gt;&lt;p&gt;I visited FUTO again quite recently as part of my research on &lt;a href=&quot;https://drewdevault.com/2025/09/24/2025-09-24-Cloudflare-and-fascists.html&quot; target=&quot;_blank&quot;&gt;Cloudflare’s donations to fascists&lt;/a&gt;, and was pleased to discover that this portfolio of grants had grown immensely since my last visit, and included a number of respectable projects that I admire and depend on (and some projects I don’t especially admire, hence arriving there during my research on FOSS projects run by fascists). But something felt fishy about this list – surely I would have heard about it if someone was going around giving big grants to projects like ffmpeg, VLC, musl libc, Tor, Managarm, Blender, NeoVim – these projects have a lot of overlap with my social group and I hadn’t heard a peep about it.&lt;/p&gt;&lt;p&gt;So I asked Rich Felker, the maintainer of &lt;a href=&quot;https://musl.libc.org/&quot; target=&quot;_blank&quot;&gt;musl libc&lt;/a&gt;, about the FUTO grant, and &lt;strong&gt;he didn’t know anything about it&lt;/strong&gt;. Rich and I spoke about this for a while and eventually Rich uncovered a transaction in his &lt;a href=&quot;https://github.com/sponsors/richfelker&quot; target=&quot;_blank&quot;&gt;GitHub sponsors&lt;/a&gt; account from FUTO: a one-time donation of $1,000. This payment circumvents musl’s established process for donations from institutional sponsors. The donation page that FUTO used includes this explanation: “This offer is for individuals, and may be available to small organizations on request. Commercial entities wishing to be listed as sponsors should inquire by email.” It’s pretty clear that there are special instructions for institutional donors who wish to receive musl’s endorsement as thanks for their contribution.&lt;/p&gt;&lt;p&gt;The extent of the FUTO “grant program”, at least in the case of musl libc, involved ignoring musl’s established process for institutional sponsors, quietly sending a modest one-time donation to one maintainer, and then plastering the logo of a well-respected open source project on a list of “grant recipients” on their home page. Rich eventually &lt;a href=&quot;https://hachyderm.io/@dalias/115259232020176340&quot; target=&quot;_blank&quot;&gt;posted on Mastodon&lt;/a&gt; to clarify that the use of the musl name and logo here was unauthorized.&lt;/p&gt;&lt;p&gt;I also asked someone I know on the ffmpeg project about the grant that they had received from FUTO and she didn’t know anything about it, either. Here’s what she said:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I’m sure we did not get a grant from them, since we tear each other to pieces over everything, and that would be enough to start a flame war. Unless some dev independently got money from them to do something, but I’m sure that we as a project got nothing. The only grant we’ve received is from the &lt;a href=&quot;https://www.sovereign.tech/&quot; target=&quot;_blank&quot;&gt;STF&lt;/a&gt; last year.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Neovim is another project FUTO lists as a grant recipient, and they also have a &lt;a href=&quot;https://neovim.io/sponsors/&quot; target=&quot;_blank&quot;&gt;separate process for institutional sponsors&lt;/a&gt;. I didn’t reach out to anyone to confirm, but FUTO does not appear on the sponsor list so presumably the M.O. is the same. This is also the case for &lt;a href=&quot;https://www.wireshark.org/members&quot; target=&quot;_blank&quot;&gt;Wireshark&lt;/a&gt;, &lt;a href=&quot;https://conduit.rs/#donate&quot; target=&quot;_blank&quot;&gt;Conduit&lt;/a&gt;, and &lt;a href=&quot;https://www.kicad.org/sponsors/sponsors/&quot; target=&quot;_blank&quot;&gt;KiCad&lt;/a&gt;. GrapheneOS is listed prominently as well, but &lt;a href=&quot;https://grapheneos.social/@GrapheneOS/113443396794247106&quot; target=&quot;_blank&quot;&gt;that doesn’t seem to have worked out very well for them&lt;/a&gt;. Presumably ffmpeg received a similar quiet donation from FUTO, rather than something more easily recognizable as a grant.&lt;/p&gt;&lt;p&gt;So, it seems like FUTO is doing some shady stuff and putting a bunch of notable FOSS projects on their home page without good reason to justify their endorsement. Who’s behind all of this?&lt;/p&gt;&lt;p&gt;As far as I can tell, the important figures are Eron Wolf&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and Louis Rossmann.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Wolf is the founder of FUTO – a bunch of money fell into his lap from founding Yahoo Games before the bottom fell out of Yahoo, and he made some smart investments to grow his wealth, which he presumably used to fund FUTO. Rossmann is a notable figure in the right to repair movement, with a large following on YouTube, who joined FUTO a year later and ultimately moved to Austin to work more closely with them. His established audience and reputation provides a marketable face for FUTO. I had heard of Rossmann prior to learning about FUTO and held him in generally good regard, despite little specific knowledge of his work, simply because we have a common cause in right to repair.&lt;/p&gt;&lt;p&gt;I hadn’t heard of Wolf before looking into FUTO. However, in the course of my research, several people tipped me off to his association with Curtis Yarvin (aka moldbug), and in particular to the use of FUTO’s platform and the credentials of Wolf and Rossman to platform and promote Yarvin. Curtis Yarvin is a full-blown, mask-off, self-proclaimed fascist. A negligible amount of due diligence is required to verify this, but here’s one source from Politico in January 2025:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I’ve interacted with Vance once since the election. I bumped into him at a party. He said, “Yarvin, you reactionary fascist.” I was like, “Thank you, Mr. Vice President, and I’m glad I didn’t stop you from getting elected.”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;– &lt;a href=&quot;https://www.politico.com/news/magazine/2025/01/30/curtis-yarvins-ideas-00201552&quot; target=&quot;_blank&quot;&gt;Ian Ward for Politico&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Vice President Vance and numerous other figures in the American right have cited Yarvin as a friend and source of inspiration in shaping policy.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; Among his many political positions, Yarvin has proclaimed that black people are genetically predisposed to a lower IQ than white people, and moreover suggests that black people are inherently suitable for enslavement.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Yarvin has appeared on FUTO’s social media channels, in particular in an interview published on &lt;a href=&quot;https://peertube.futo.org/w/eSzKtjupL928QzxPXC5k2R&quot; target=&quot;_blank&quot;&gt;PeerTube&lt;/a&gt; and &lt;a href=&quot;https://odysee.com/@FUTO:e/curtisyarvin:2&quot; target=&quot;_blank&quot;&gt;Odysee&lt;/a&gt;, the latter a platform controversial for its role in spreading hate speech and misinformation.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; Yarvin &lt;a href=&quot;https://www.youtube.com/watch?v=iFn5t4etqNg&quot; target=&quot;_blank&quot;&gt;also appeared on stage&lt;/a&gt; to “debate” Louis Rossmann in June 2022, in which Yarvin is permitted to speak at length with minimal interruptions or rebuttals to argue for an authoritarian techno-monarchy to replace democracy.&lt;/p&gt;&lt;p&gt;Rossmann caught some flack for this “debate” and gave a milquetoast response in &lt;a href=&quot;https://www.youtube.com/watch?v=iFn5t4etqNg&amp;lc=Ugx7ba2sI30CGEylY_R4AaABAg&quot; target=&quot;_blank&quot;&gt;a YouTube comment&lt;/a&gt; on this video, explaining that he agreed to this on very short notice as a favor to Eron, who had donated “a million” to Rossmann’s non-profit prior to bringing Rossmann into the fold at FUTO. Rossmann does rebuke Yarvin’s thesis, albeit buried in this YouTube comment rather than when he had the opportunity to do so on-stage during the debate. Don’t argue with fascists, Louis – they aren’t arguing with &lt;em&gt;you&lt;/em&gt;, they are pitching their ideas&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fn-7-ref-1&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; to &lt;em&gt;the audience&lt;/em&gt;. Smart fascists are experts at misdirection and bad-faith debate tactics and as a consequence Rossmann just becomes a vehicle for fascist propaganda – consult the YouTube comments to see who this video resonates with the most.&lt;/p&gt;&lt;p&gt;In the end, Rossmann seems to regret agreeing to this debate. I don’t think that Eron Wolf regrets it, though – based on his facilitation of this debate and his own interview with Yarvin on the FUTO channel a month later, I can only assume that Wolf considers Yarvin a close associate. No surprise given that Wolf is precisely the kind of insecure silicon valley techbro Yarvin’s rhetoric is designed to appeal to – moderately wealthy but unknown, and according to Yarvin, fit to be a king. Rossmann probably needs to reflect on why he associates with and lends his reputation to an organization that openly and unapologetically platforms its founder’s fascist friends.&lt;/p&gt;&lt;p&gt;In summary, FUTO is not just the product of some eccentric who founded a grant-making institution that funds open source at the cost of making us read his weird manifestos on free markets and oligopoly. It’s a private, for-profit company that associates with and uses their brand to promote fascists. They push an open-washing narrative and they portray themselves as a grant-making institution when, in truth, they’re passing off a handful of small donations as if they were endorsements from dozens of respectable, high-profile open source projects, in an attempt to legitimize themselves, and, indirectly, legitimize people they platform like Curtis Yarvin.&lt;/p&gt;&lt;p&gt;So, if you read this and discover that &lt;em&gt;your&lt;/em&gt; project’s name and logo is being proudly displayed &lt;a href=&quot;https://futo.org&quot; target=&quot;_blank&quot;&gt;on the front page&lt;/a&gt; of a fascist-adjacent, washed-up millionaire’s scummy vanity company, and you don’t like that, maybe you should ask them to knock it off? Eron, Louis – you know that a lot of these logos are trademarked, right?&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;Updated 2025-10-27&lt;/em&gt;:&lt;/p&gt;&lt;p&gt;The FUTO website has been updated to clarify the nature of grants versus donations (&lt;a href=&quot;https://web.archive.org/web/20251022223705/https://www.futo.org/&quot; target=&quot;_blank&quot;&gt;before&lt;/a&gt;, &lt;a href=&quot;https://futo.org&quot; target=&quot;_blank&quot;&gt;after&lt;/a&gt;) and to reduce the appearance of endorsements from the donation recipients – the site is much better after this change.&lt;/p&gt;&lt;p&gt;I spoke to a representative who spoke for the FUTO leadership, and shared positive feedback regarding the changes to the website. I also asked for a follow-up on the matter of platforming fascist activist Curtis Yarvin on their social media channels, and I was provided this official response:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We prefer to spend our time building great software for people to use, funding interesting projects, and making FUTO better every day. We have no interest in engaging in politics as this is a distraction from our work. We’d like to move past this. We don’t have any further comment that that.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I understand the view that distancing oneself from politics can be a productive approach to your work, and the work FUTO does funding great software like Immich is indeed important work that should be conducted relatively free of distractions. However, FUTO is a fundamentally political organization and it does not distance itself from politics. Consider for example the &lt;a href=&quot;https://futo.org/about/futo-statement-on-opensource/&quot; target=&quot;_blank&quot;&gt;FUTO statement on Open Source&lt;/a&gt;, which takes several political positions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Disapproval of the Open Source Initiative and their legitimacy as an authority&lt;/li&gt;&lt;li&gt;Disapproval of OSI’s proposed AI standards&lt;/li&gt;&lt;li&gt;Disapproval of the “tech oligopoly”&lt;/li&gt;&lt;li&gt;Advocacy for an “open source” which is inclusive of restrictions on commercial use&lt;/li&gt;&lt;li&gt;Support for Eric S. Raymond’s side in his conflict with OSI&lt;/li&gt;&lt;li&gt;Tacit support for Bryan Lunduke&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A truer “apolitical” approach would accept the mainstream definition of open source, would not take positions on conflicts with OSI or Eric Raymond, and would be careful not to cite (or &lt;a href=&quot;https://www.youtube.com/watch?v=6RU7FZpd_K0&quot; target=&quot;_blank&quot;&gt;platform&lt;/a&gt;) controversial figures such as Lunduke.&lt;/p&gt;&lt;p&gt;It is difficult for FUTO’s work to be apolitical at all. Importantly, there are biases in their grants and donations: their selections have a tendency to privilege projects focused on privacy, decentralization, multimedia, communication, and right to repair, all of which suggest the political priorities of FUTO. The choice to fund “source first” software in addition to open source, or not to fund outright closed source software, or not to vet projects based on their community moderation, are also political factors in their process of selecting funding recipients, or at least are seemingly apolitical decisions which ultimately have political consequences.&lt;/p&gt;&lt;p&gt;This brings us to the political nature of the choice to platform Curtis Yarvin. Yarvin is a self-proclaimed fascist who argues openly for fascist politics. Platforming Yarvin on the FUTO channels legitimizes Yarvin’s ideas and his work, and provides curious listeners a funnel that leads to Yarvin’s more radical ideas and into a far-right rabbit-hole. Platforming Yarvin advances the fascist political program.&lt;/p&gt;&lt;p&gt;It should go without saying that it is political to support fascism or fascists. There is an outrageous moral, intellectual, and political contradiction in claiming that it is apolitical to promote a person whose political program is to dismantle democracy and eject people he disagrees with from the political sphere entirely. FUTO should reflect on their values, acknowledge the political nature of their work, and consider the ways in which their work intersects with politics writ large, then make decisions that align their political actions with their political beliefs.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Whats-up-with-FUTO/</link>
        
        <pubDate>Wed, 22 Oct 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Whats-up-with-FUTO/</guid>
      </item>
    
      <item>
        
        
          <title>Cloudflare bankrolls fascists</title>
          <description>
            &lt;p&gt;US politics has been pretty fascist lately. The state is filling up concentration camps, engaging in mass state violence against people on the basis of racialized traits, deporting them to random countries without any respect for habeas corpus, exerting state pressure on the free press to censor speech critical of the current administration, and Trump is openly floating the idea of an unconstitutional third term.&lt;/p&gt;&lt;p&gt;Fascism is clearly on the rise, and they’re winning more and more power. None of this is far removed from us in the FOSS community – there are a number of fascists working in FOSS, same as the rest of society. I don’t call them fascists baselessly – someone who speaks out in support of and expresses solidarity with fascists, or who uses fascists dog-whistles or promotes fascist ideology and talking points, or boosts fascist conspiracy theories – well, they’re a fascist.&lt;/p&gt;&lt;p&gt;If one consistently speaks in support of a certain political position and against the opponents of that position then it is correct to identify them with this political position. Facts, as it were, don’t care about feelings, namely the feelings that get hurt when someone is called a fascist. Fascists naturally do not want to be identified as such and will reject the label, but we shouldn’t take their word for it. People should be much more afraid of being called out as fascist than they are afraid of calling someone a fascist. If someone doesn’t want to be called a fascist, they shouldn’t act like one.&lt;/p&gt;&lt;p&gt;It’s in this disturbing political context that I saw an odd post from the Cloudflare blog pop up in my circles this week: &lt;a href=&quot;https://blog.cloudflare.com/supporting-the-future-of-the-open-web/&quot; target=&quot;_blank&quot;&gt;Supporting the future of the open web: Cloudflare is sponsoring Ladybird and Omarchy&lt;/a&gt;. Based on &lt;a href=&quot;https://ladybird.org/#faq&quot; target=&quot;_blank&quot;&gt;Ladybird’s sponsorship terms&lt;/a&gt; we can assume that these projects received on the order of $100,000 USD from Cloudflare. I find this odd for a few reasons, in particular because one thing that I know these two projects have in common is that they are both run by fascists.&lt;/p&gt;&lt;p&gt;Even at face value this is an unusual pair of projects to fund. I’m all for FOSS projects getting funded, of course, and I won’t complain about a project’s funding on the solitary basis that it’s an odd choice. I will point out that these are odd choices, though, especially Omarchy.&lt;/p&gt;&lt;p&gt;Ladybird makes some sense, given that it’s aligned in principle with Cloudflare’s stated objective to “support the open web”, though I remain bearish that new web browser engines are even &lt;a href=&quot;https://drewdevault.com/2020/03/18/Reckless-limitless-scope.html&quot; target=&quot;_blank&quot;&gt;possible to make&lt;/a&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Omarchy is a bizarre choice, though – do we really need another pre-customized Arch Linux distribution? And if we do, do we really need a big corporation like Cloudflare to bankroll it? Everyone on /r/unixporn manages to make Arch Linux look pretty for free.&lt;/p&gt;&lt;p&gt;Omarchy is a &lt;em&gt;very&lt;/em&gt; weird project to fund, come to think of it. Making an Arch Linux spin technically requires &lt;em&gt;some&lt;/em&gt; work, and work is work, I won’t deny it, but most of the work done here is from Arch Linux and Hyprland. Why not fund those, instead? Well, don’t fund Hyprland, since it’s also &lt;a href=&quot;https://drewdevault.com/2023/09/17/Hyprland-toxicity.html&quot; target=&quot;_blank&quot;&gt;run by&lt;/a&gt; a &lt;a href=&quot;https://drewdevault.com/2024/04/09/2024-04-09-FDO-conduct-enforcement.html&quot; target=&quot;_blank&quot;&gt;bunch of fascists&lt;/a&gt;, but you get my point.&lt;/p&gt;&lt;p&gt;Anyway, Omarchy and Ladybird are both run by fascists. Omarchy makes this pretty obvious from the outset – on &lt;a href=&quot;https://omarchy.org/&quot; target=&quot;_blank&quot;&gt;the home page&lt;/a&gt; the big YouTube poster image &lt;a href=&quot;https://redacted.moe/f/ba0d7a6b.png&quot; target=&quot;_blank&quot;&gt;prominently features&lt;/a&gt; SuperGrok, which is a pathetically transparent dog-whistle to signal alliance with Elon Musk’s fascist politics. Omarchy is the pet project of David Heinemeier Hansson, aka DHH, who is well known as a rich &lt;a href=&quot;https://davidcel.is/articles/rails-needs-new-governance&quot; target=&quot;_blank&quot;&gt;fascist&lt;/a&gt; &lt;a href=&quot;https://tekin.co.uk/2025/09/the-ruby-community-has-a-dhh-problem&quot; target=&quot;_blank&quot;&gt;weirdo&lt;/a&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; One need only consult &lt;a href=&quot;https://world.hey.com/dhh&quot; target=&quot;_blank&quot;&gt;his blog&lt;/a&gt; to browse his &lt;a href=&quot;https://world.hey.com/dhh/as-i-remember-london-e7d38e64&quot; target=&quot;_blank&quot;&gt;weird, racist views on immigration&lt;/a&gt;, &lt;a href=&quot;https://world.hey.com/dhh/the-beauty-of-ideals-b3dccf72&quot; target=&quot;_blank&quot;&gt;fat-shaming objections to diverse representation&lt;/a&gt;, &lt;a href=&quot;https://world.hey.com/dhh/the-parental-dead-end-of-consent-morality-e4e8a8ee&quot; target=&quot;_blank&quot;&gt;vaguely anti-feminist/homophobic/rapey rants on consent&lt;/a&gt;, and, recently, &lt;a href=&quot;https://world.hey.com/dhh/words-are-not-violence-c751f14f&quot; target=&quot;_blank&quot;&gt;tone-policing antifascists&lt;/a&gt; who celebrate the death of notable fascist Charlie Kirk.&lt;/p&gt;&lt;p&gt;Speaking of tributes to Charlie Kirk, that brings us to Andreas Kling, the project lead for Ladybird, who tweeted on the occasion of his assassination:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;RIP Charlie Kirk&lt;/p&gt;&lt;p&gt;I hope many more debate nerds carry on his quest to engage young people with words, not fists.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;– &lt;a href=&quot;https://nitter.net/awesomekling/status/1966456391146606806&quot; target=&quot;_blank&quot;&gt;@awesomekling&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Kling has had a few things to say about Kirk on Twitter lately. &lt;a href=&quot;https://nitter.net/awesomekling/status/1967178708852097278&quot; target=&quot;_blank&quot;&gt;Here’s another one&lt;/a&gt; – give you three guesses as to which “[group]” he objects to punching. You may also recall that Kling achieved some notoriety for his obnoxious response as the maintainer of SerenityOS when someone proposed gender-neutral language for the documentation:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/SerenityOS/serenity/pull/6814&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/d57df43b.png&quot;&gt;
&lt;figcaption&gt;Screenshot of the interaction on GitHub. Kling responds “This project is not an appropriate arena to advertise your personal politics.”&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Replacing “he” with “they” in one sentence of the documentation is the kind of “ideologically motivated change” that Serenity’s &lt;a href=&quot;https://github.com/SerenityOS/serenity/blob/master/CONTRIBUTING.md#on-ideologically-motivated-changes&quot; target=&quot;_blank&quot;&gt;CONTRIBUTING.md&lt;/a&gt; apparently aims to prevent, a classic case of the sexist “identities that are not men are inherently political” nonsense. Ladybird has a &lt;a href=&quot;https://github.com/LadybirdBrowser/ladybird/blob/master/CONTRIBUTING.md#on-neutrality&quot; target=&quot;_blank&quot;&gt;similar, weirdly defensive policy&lt;/a&gt; on “neutrality”, and a milquetoast &lt;a href=&quot;https://github.com/LadybirdBrowser/ladybird/blob/master/CODE_OF_CONDUCT.md&quot; target=&quot;_blank&quot;&gt;code of conduct&lt;/a&gt;, which is based on the Ruby Community Conduct Guideline, which has been itself the subject of many controversies due to its inadequacy leading to real-world incidents of harassment and abuse.&lt;/p&gt;&lt;p&gt;Here’s another one – Kling endorsing white replacement theory in June:&lt;/p&gt;&lt;blockquote&gt;&lt;blockquote&gt;&lt;p&gt;White males are actively discriminated against in tech.&lt;/p&gt;&lt;p&gt;It’s an open secret of Silicon Valley.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;One of the last meetings I attended before leaving Apple (in 2017) was management asking us to “keep the corporate diversity targets in mind” when interviewing potential new hires.&lt;/p&gt;&lt;p&gt;The phrasing was careful, but the implication was pretty clear.&lt;/p&gt;&lt;p&gt;I knew in my heart this wasn’t wholesome, but I was too scared to rock the boat at the time.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;– &lt;a href=&quot;https://nitter.net/awesomekling/status/1874518295350837401&quot; target=&quot;_blank&quot;&gt;@awesomekling&lt;/a&gt; replying to @danheld&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;And in a moment of poetic irony, a few days ago Kling &lt;a href=&quot;https://nitter.net/awesomekling/status/1969350008538370216&quot; target=&quot;_blank&quot;&gt;spoke in solidarity&lt;/a&gt; with Hansson over his “persecution” for “banal, mainstream positions” on Twitter just a few days ago, in response to Hansson’s tweet signal-boosting another &lt;a href=&quot;https://www.youtube.com/watch?v=mhqeuO9RKKk&quot; target=&quot;_blank&quot;&gt;notable reactionary tech fascist&lt;/a&gt;, Bryan Lunduke.&lt;/p&gt;&lt;p&gt;So, to sum it up, Kling wears his mask a bit better than Hansson, but as far as I’m concerned it seems clear that both projects are run by fascists. If it walks like a fascist and quacks like a fascist… then why is Cloudflare giving them hundreds of thousands of dollars?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Cloudflare-and-fascists/</link>
        
        <pubDate>Wed, 24 Sep 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Cloudflare-and-fascists/</guid>
      </item>
    
      <item>
        
        
          <title>A better future for JavaScript that won&apos;t happen</title>
          <description>
            &lt;p&gt;In the wake of &lt;a href=&quot;https://www.ox.security/blog/npm-2-0-hack-40-npm-packages-hit-in-major-supply-chain-attack/&quot; target=&quot;_blank&quot;&gt;the largest supply-chain attack in history&lt;/a&gt;, the JavaScript community could have a moment of reckoning and decide: never again. As the panic and shame subsides, after compromised developers finish re-provisioning their workstations and rotating their keys, the ecosystem might re-orient itself towards solving the fundamental flaws that allowed this to happen.&lt;/p&gt;&lt;p&gt;After all, people have been sounding the alarm &lt;a href=&quot;https://drewdevault.com/2019/12/09/Developers-shouldnt-distribute.html&quot; target=&quot;_blank&quot;&gt;for years&lt;/a&gt; that this approach to dependency management is &lt;a href=&quot;https://drewdevault.com/2021/09/27/Let-distros-do-their-job.html&quot; target=&quot;_blank&quot;&gt;reckless&lt;/a&gt; and &lt;a href=&quot;https://drewdevault.com/2022/05/12/Supply-chain-when-will-we-learn.html&quot; target=&quot;_blank&quot;&gt;dangerous&lt;/a&gt; and broken by design. Maybe this is the moment when the JavaScript ecosystem begins to understand the importance and urgency of this problem, and begins its course correction. It could leave behind its sprawling dependency trees full of micro-libraries, establish software distribution based on relationships of trust, and incorporate the decades of research and innovation established by more serious dependency management systems.&lt;/p&gt;&lt;p&gt;Perhaps Google and Mozilla, leaders in JavaScript standards and implementations, will start developing a real standard library for JavaScript, which makes micro-dependencies like left-pad a thing of the past. This could be combined with a consolidation of efforts, merging micro-libraries into larger packages with a more coherent and holistic scope and purpose, which prune their own dependency trees in turn.&lt;/p&gt;&lt;p&gt;This could be the moment where npm comes to terms with its broken design, and with a well-funded effort (recall that, ultimately, npm is GitHub is Microsoft, market cap $3 trillion USD), will develop and roll out the next generation of package management for JavaScript. It could incorporate the practices developed and proven in Linux distributions, which rarely suffer from these sorts of attacks, by de-coupling development from packaging and distribution, establishing package maintainers who assemble and distribute curated collections of software libraries. By introducing universal signatures for packages of executable code, smaller channels and webs of trust, reproducible builds, and the many other straightforward, obvious techniques used by responsible package managers.&lt;/p&gt;&lt;p&gt;Maybe other languages that depend on this broken dependency management model, like Cargo, PyPI, RubyGems, and many more, are watching this incident and know that the very same crisis looms in their future. Maybe they will change course, too, before the inevitable.&lt;/p&gt;&lt;p&gt;Imagine if other large corporations who depend on and profit from this massive pile of recklessly organized software committed their money and resources to it, through putting their engineers to the task of fixing these problems, through coming together to establish and implement new standards, through direct funding of their dependencies and by distributing money through institutions like NLNet, ushering in an era of responsible, sustainable, and secure software development.&lt;/p&gt;&lt;p&gt;This would be a good future, but it’s not the future that lies in wait for us. The future will be more of the same. Expect symbolic gestures – mandatory 2FA will be rolled out in more places, certainly, and the big players will write off meager donations in the name of “OSS security and resilience” in their marketing budgets.&lt;/p&gt;&lt;p&gt;No one will learn their lesson. This has been happening for decades and no one has learned anything from it yet. This is the defining hubris of this generation of software development.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/An-impossible-future-for-JS/</link>
        
        <pubDate>Wed, 17 Sep 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/An-impossible-future-for-JS/</guid>
      </item>
    
      <item>
        
        
          <title>Embedding Wren in Hare</title>
          <description>
            &lt;p&gt;I’ve been on the lookout for a scripting language which can be neatly embedded into Hare programs. Perhaps the obvious candidate is &lt;a href=&quot;https://www.lua.org/&quot; target=&quot;_blank&quot;&gt;Lua&lt;/a&gt; – but I’m not particularly enthusiastic about it. When I was evaluating the landscape of tools which are “like Lua, but not Lua”, I found an interesting contender: &lt;a href=&quot;https://wren.io/&quot; target=&quot;_blank&quot;&gt;Wren&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I found that Wren punches far above its weight for such a simple language. It’s object oriented, which, you know, take it or leave it depending on your use-case, but it’s very straightforwardly interesting for what it is. I found a few things to complain about, of course – its scope rules are silly, the C API has some odd limitations here and there, and in my opinion the “standard library” provided by wren CLI is poorly designed. But, surprisingly, my list of complaints more or less ends there, and I was excited to build a nice interface to it from Hare.&lt;/p&gt;&lt;p&gt;The result is &lt;a href=&quot;https://wren.builtwithhare.org&quot; target=&quot;_blank&quot;&gt;hare-wren&lt;/a&gt;. Check it out!&lt;/p&gt;&lt;p&gt;The basic Wren C API is relatively straightforwardly exposed to Hare via the wren module, though I elected to mold it into a more idiomatic Hare interface rather than expose the C API directly to Hare. You can use it something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vm&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdio_config&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;interpret&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;`
		System.print(&amp;quot;Hello world!&amp;quot;)
	`&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;&lt;pre&gt;&lt;code&gt;$ hare run -lc main.ha
Hello world!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Calling Hare from Wren and vice-versa is also possible with hare-wren, of course. Here’s another example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdio_config&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;bind_foreign_method&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bind_foreign_method&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vm&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;interpret&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;`
	class Example {
		foreign static greet(user)
	}

	System.print(Example.greet(&amp;quot;Harriet&amp;quot;))
	`&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;bind_foreign_method&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;class_name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;is_static&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;foreign_method_fn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;is_valid&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;class_name&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Example&amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;greet(_)&amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;is_static&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;is_valid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;greet_user&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;greet_user&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;get_string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;greeting&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;asprintf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Hello, {}!&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;wren&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;set_string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;&lt;pre&gt;&lt;code&gt;$ hare run -lc main.ha
Hello, Harriet!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In addition to exposing the basic Wren virtual machine to Hare, hare-wren has an optional submodule, wren::api, which implements a simple async runtime based on &lt;a href=&quot;https://sr.ht/~sircmpwn/hare-ev&quot; target=&quot;_blank&quot;&gt;hare-ev&lt;/a&gt; and a modest “standard” library, much like &lt;a href=&quot;https://wren.io/cli/&quot; target=&quot;_blank&quot;&gt;Wren CLI&lt;/a&gt;. I felt that the Wren CLI libraries had a lot of room for improvement, so I made the call to implement a standard library which is only somewhat compatible with Wren CLI.&lt;/p&gt;&lt;p&gt;On top of the async runtime, Hare’s wren::api runtime provides some basic features for reading and writing files, querying the process arguments and environment, etc. It’s not much but it is, perhaps, an interesting place to begin building out something a bit more interesting. A simple module loader is also included, which introduces some conventions for installing third-party Wren modules that may be of use for future projects to add new libraries and such.&lt;/p&gt;&lt;p&gt;Much like wren-cli, hare-wren also provides the &lt;code&gt;hwren&lt;/code&gt; command, which makes this runtime, standard library, and module loader conveniently available from the command line. It does not, however, support a REPL at the moment.&lt;/p&gt;&lt;p&gt;I hope you find it interesting! I have a few projects down the line which might take advantage of hare-wren, and it would be nice to expand the wren::api library a bit more as well. If you have a Hare project which would benefit from embedding Wren, please let me know – and consider sending some patches to improve it!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Hare-and-Wren/</link>
        
        <pubDate>Wed, 20 Aug 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Hare-and-Wren/</guid>
      </item>
    
      <item>
        
        
          <title>What&apos;s new with Himitsu 0.9?</title>
          <description>
            &lt;p&gt;Last week, Armin and I worked together on the latest release of &lt;a href=&quot;https://himitsustore.org/&quot; target=&quot;_blank&quot;&gt;Himitsu&lt;/a&gt;, a “secret storage manager” for Linux. I haven’t blogged about Himitsu since I announced it &lt;a href=&quot;https://drewdevault.com/blog/Himitsu/&quot;&gt;three years ago&lt;/a&gt;, and I thought it would be nice to give you a closer look at the latest release, both for users eager to see the latest features and for those who haven’t been following along.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;A brief introduction: Himitsu is like a password manager, but more general: it stores any kind of secret in its database, including passwords but also SSH keys, credit card numbers, your full disk encryption key, answers to those annoying “security questions” your bank obliged you to fill in, and so on. It can also enrich your secrets with arbitrary metadata, so instead of just storing, say, your IMAP password, it can also store the host, port, TLS configuration, and username, storing the complete information necessary to establish an IMAP session.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Another important detail: Himitsu is written in Hare and depends on Hare’s native implementations of cryptographic primitives – neither Himitsu nor the cryptography implementation it depends on have been independently audited.&lt;/em&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;So, what new and exciting features does Himitsu 0.9 bring to the table? Let me summarize the highlights for you.&lt;/p&gt;&lt;h2&gt;A new prompter&lt;/h2&gt;&lt;p&gt;The face of Himitsu is the prompter. The core Himitsu daemon has no user interface and only communicates with the outside world through its IPC protocols. One of those protocols is the “prompter”, which Himitsu uses to communicate with the user, to ask you for consent to use your secret keys, to enter the master password, and so on. The prompter is decoupled from the daemon so that it is easy to substitute with different versions which accommodate different use-cases, for example by integrating the prompter more deeply into a desktop environment or to build one that fits better on a touch screen UI like a phone.&lt;/p&gt;&lt;p&gt;But, in practice, given Himitsu’s still-narrow adoption, most people use the GTK+ prompter developed upstream. Until recently, the prompter was written in Python for GTK+ 3, and it was a bit janky and stale. The new &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hiprompt-gtk&quot; target=&quot;_blank&quot;&gt;hiprompt-gtk&lt;/a&gt; changes that, replacing it with a new GTK4 prompter implemented in Hare.&lt;/p&gt;&lt;p&gt;&lt;video src=&quot;https://redacted.moe/f/176df4cc.webm&quot; autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;I’m excited to share this one with you – it was personally my main contribution to this release. The prompter is based on Alexey Yerin’s &lt;a href=&quot;https://git.sr.ht/~yerinalexey/hare-gi&quot; target=&quot;_blank&quot;&gt;hare-gi&lt;/a&gt;, which is a (currently only prototype-quality) code generator which processes GObject Introspection documents into Hare modules that bind to libraries like GTK+. The prompter uses &lt;a href=&quot;https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/&quot; target=&quot;_blank&quot;&gt;Adwaita&lt;/a&gt; for its aesthetic and controls and &lt;a href=&quot;https://github.com/wmww/gtk4-layer-shell&quot; target=&quot;_blank&quot;&gt;GTK layer shell&lt;/a&gt; for smoother integration on supported Wayland compositors like Sway.&lt;/p&gt;&lt;h2&gt;Secret service integration&lt;/h2&gt;&lt;p&gt;Armin has been hard at work on a new package, &lt;a href=&quot;https://git.sr.ht/~apreiml/himitsu-secret-service&quot; target=&quot;_blank&quot;&gt;himitsu-secret-service&lt;/a&gt;, which provides the long-awaited support for integrating Himitsu with the dbus Secret Service API used by many Linux applications to manage secret keys. This makes it possible for Himitsu to be used as a secure replacement for, say, gnome-keyring.&lt;/p&gt;&lt;h2&gt;Editing secret keys&lt;/h2&gt;&lt;p&gt;Prior to this release, the only way to edit a secret key was to remove it and re-add it with the desired edits applied manually. This was a tedious and error-prone process, especially when bulk-editing keys. This release includes some work from Armin to improve the process, by adding a “change” request to the IPC protocol and implementing it in the command line hiq client.&lt;/p&gt;&lt;p&gt;For example, if you changed your email address, you could update all of your logins like so:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ hiq -c email=newemail@example.org email=oldemail@example.org
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Don’t worry about typos or mistakes – the new prompter will give you a summary of the changes for your approval before the changes are applied.&lt;/p&gt;&lt;p&gt;You can also do more complex edits with the -e flag – check out the hiq(1) man page for details.&lt;/p&gt;&lt;h2&gt;Secret reuse notifications&lt;/h2&gt;&lt;p&gt;Since version 0.8, Himitsu has supported “remembering” your choice, for supported clients, to consent to the use of your secrets. This allows you, for example, to remember that you agreed for the &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu-ssh&quot; target=&quot;_blank&quot;&gt;SSH agent&lt;/a&gt; to use your SSH keys for an hour, or for the duration or your login session, etc. Version 0.9 adds a minor improvement to this feature – you can add a command to himitsu.ini, such as notify-send, which will be executed whenever a client takes advantage of this “remembered” consent, so that you can be notified whenever your secrets are used again, ensuring that any unexpected use of your secrets will get your attention.&lt;/p&gt;&lt;h2&gt;himitsu-firefox improvements&lt;/h2&gt;&lt;p&gt;There are also some minor improvements landed for &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu-firefox&quot; target=&quot;_blank&quot;&gt;himitsu-firefox&lt;/a&gt; that I’d like to note. tiosgz sent us a nice patch which makes the identification of login fields in forms more reliable – thanks! And I’ve added a couple of useful programs, himitsu-firefox-import and himitsu-firefox-export, which will help you move logins between Himitsu and Firefox’s native password manager, should that be useful to you.&lt;/p&gt;&lt;h2&gt;And the rest&lt;/h2&gt;&lt;p&gt;Check out the &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu/refs/0.9&quot; target=&quot;_blank&quot;&gt;changelog&lt;/a&gt; for the rest of the improvements. Enjoy!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Whats-new-with-himitsu/</link>
        
        <pubDate>Fri, 08 Aug 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Whats-new-with-himitsu/</guid>
      </item>
    
      <item>
        
        
          <title>Just speak the truth</title>
          <description>
            &lt;p&gt;Today, we’re looking at two case studies in how to respond when reactionaries appear in your free software community.&lt;/p&gt;&lt;h2&gt;Exhibit A&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;It is a technical decision.&lt;/p&gt;&lt;p&gt;The technical reason is that the security team does not have the bandwidth to provide lifecycle maintenance for multiple X server implementations. Part of the reason for moving X from main to community was to reduce the burden on the security team for long-term maintenance of X. Additionally, nobody so far on the security team has expressed any interest in collaborating with &lt;span class=&quot;redacted &quot;&gt;xxxxxx&lt;/span&gt; on security concerns.&lt;/p&gt;&lt;p&gt;We have a working relationship with Freedesktop already, while we would have to start from the beginning with &lt;span class=&quot;redacted &quot;&gt;xxxxxx&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;Why does nobody on the security team have any interest in collaboration with &lt;span class=&quot;redacted &quot;&gt;xxxxxx&lt;/span&gt;? Well, speaking for myself only here – when I looked at their official chat linked in their README, I was immediately greeted with alt-right propaganda rather than tactically useful information about &lt;span class=&quot;redacted &quot;&gt;xxxxxx&lt;/span&gt; development. At least for me, I don’t have any interest in filtering through hyperbolic political discussions to find out about CVEs and other relevant data for managing the security lifecycle of X.&lt;/p&gt;&lt;p&gt;Without relevant security data products from &lt;span class=&quot;redacted &quot;&gt;xxxxxx&lt;/span&gt;, as well as a professionally-behaving security contact, it is unlikely for &lt;span class=&quot;redacted &quot;&gt;xxxxxx&lt;/span&gt; to gain traction in any serious distribution, because X is literally one of the more complex stacks of software for a security team to manage already.&lt;/p&gt;&lt;p&gt;At the same time, I sympathize with the need to keep X alive and in good shape, and agree that there hasn’t been much movement from freedesktop in maintaining X in the past few years. There are many desktop environments which will never get ported to Wayland and we do need a viable solution to keep those desktop environments working.&lt;/p&gt;&lt;/blockquote&gt;&lt;style&gt;
.redacted {
  color: black;
  background: black;
}
&lt;/style&gt;
&lt;p&gt;I know the person who wrote this, and I know that she’s a smart cookie, and therefore I know that she probably understood at a glance that the community behind this “project” literally wants to lynch her. In response, she takes the high road, avoids confronting the truth directly, and gives the trolls a bunch of talking points to latch on for counter-arguments. Leaves plenty of room for them to bog everyone down in concern trolling and provides ample material to fuel their attention-driven hate machine.&lt;/p&gt;&lt;p&gt;There’s room for improvement here.&lt;/p&gt;&lt;h2&gt;Exhibit B&lt;/h2&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/d9dd3368.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a post by Chimera Linux which reads “any effort to put (redacted) in chimera will be rejected on the technical basis of the maintainers being reactionary dipshits”&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Concise, speaks the truth, answers ridiculous proposals with ridicule, does not afford the aforementioned reactionary dipshits an opportunity to propose a counter-argument. A+.&lt;/p&gt;&lt;p&gt;Extra credit for the follow-up:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/965aa15b.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a follow-up post that reads “just to be clear, given the coverage of the most recent post, we don’t want to be subject to any conspiracy theories arising from that. so i’ll just use this opportunity to declare that we are definitely here to further woke agenda by turning free software gay”&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;The requirement for a passing grade in this class is a polite but summary dismissal, but additional credit is awarded for anyone who does not indulge far-right agitators as if they were equal partners in maintaining a sense of professional decorum.&lt;/p&gt;&lt;p&gt;If you are a community leader in FOSS, you are not obligated to waste your time coming up with a long-winded technical answer to keep nazis out of your community. They want you to argue with them and give them attention and feed them material for their reactionary blog or whatever. Don’t fall into their trap. Do not answer bad faith with good faith. This is a skill you need to learn in order to be an effective community leader.&lt;/p&gt;&lt;p&gt;If you see nazis 👏👏 you ban nazis 👏👏 — it’s as simple as that.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;The name of the project is censored not because it’s particularly hard for you to find, but because all they really want is attention, and you and me are going to do each other a solid by not giving them any of that directly.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;To preclude the sorts of reply guys who are going to insist on name-dropping the project and having a thread about the underlying drama in the comments, the short introduction is as follows:&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;For a few years now, a handful of reactionary trolls have been stoking division in the community by driving a wedge between X11 and Wayland users, pushing a conspiracy theory that paints RedHat as the DEI boogeyman of FOSS and assigning reactionary values to X11 and woke (pejorative) values to Wayland. Recently, reactionary opportunists “forked” Xorg, replaced all of the literature with political manifestos and &lt;abbr title=&quot;Coded language that signals in-group status between reactionaries and hate groups while appearing benign to outsiders who don&apos;t recognize their dialect. &apos;DEI&apos; is an example of a dog-whistle: it appears overtly benign, but reactionaries use it when they want to say the N-word in polite company.&quot;&gt;dog-whistles&lt;/abbr&gt;, then used it as a platform to start shit with downstream Linux distros by petitioning for inclusion and sending &lt;abbr title=&quot;People who argue in bad faith to disrupt discussions by tactically dwelling on tone, rhetoric, philosophy, or procedural issues. Often they have no prior affiliation with the community and show up just to cause trouble, taking advantage of the better nature of hapless moderators and bystanders to further the aim of helping trolls fly under the radar and shifting the Overton window of a community to the right.&quot;&gt;concern trolls&lt;/abbr&gt; to waste everyone’s time.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;The project itself is of little consequence; they serve our purposes today by providing us with case-studies in dealing with reactionary idiots starting shit in your community.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Speak-the-truth/</link>
        
        <pubDate>Mon, 30 Jun 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Speak-the-truth/</guid>
      </item>
    
      <item>
        
        
          <title>Unionize or die</title>
          <description>
            &lt;p&gt;Tech workers have long resisted the suggestion that we should be organized into unions. The topic is consistently met with a cold reception by tech workers when it is raised, and no big tech workforce is meaningfully organized. This is a fatal mistake – and I don’t mean “fatal” in the figurative sense. Tech workers, it’s time for you to unionize, and strike, or you and your loved ones are literally going to die.&lt;/p&gt;&lt;p&gt;In this article I will justify this statement and show that it is clearly not hyperbolic. I will explain exactly what you need to do, and how organized labor can and will save your life.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Hey – if you want to get involved in labor organizing in the tech sector you should consider joining the new &lt;a href=&quot;https://unitelabor.dev&quot; target=&quot;_blank&quot;&gt;unitelabor.dev&lt;/a&gt; forum. Adding a head’s up here in case you don’t make it to the end of this very long blog post.&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;The imperative to organize is your economic self-interest&lt;/h2&gt;&lt;p&gt;Before I talk about the threats to your life and liberty that you must confront through organized labor, let me re-iterate the economic position for unionizing your workplace. It is important to revisit this now, because the power politics of the tech sector has been rapidly changing over the past few years, and those changes are not in your favor.&lt;/p&gt;&lt;p&gt;The tech industry bourgeoisie has been waging a prolonged war on labor for at least a decade. Far from mounting any kind of resistance, most of tech labor doesn’t even understand that this is happening to them. Your boss is obsessed with making you powerless and replaceable. You may not realize how much leverage you have over your boss, but your boss certainly does – and has been doing everything in their power to undermine you before you wizen up. Don’t let yourself believe you’re a part of their club – if your income depends on your salary, you are part of the working class.&lt;/p&gt;&lt;p&gt;Payroll – that’s you – is the single biggest expense for every tech company. When tech capitalists look at their balance sheet and start thinking of strategies for increasing profits, they see an awful lot of pesky zeroes stacked up next to the line item for payroll and benefits. Long-term, what’s their best play?&lt;/p&gt;&lt;p&gt;It starts with funneling cash and influence into educating a bigger, cheaper generation of compsci graduates to flood the labor market – “everyone can code”. Think about strategic investments in cheap(ish), broadly available courses, online schools and coding “bootcamps” – dangling your high salary as the carrot in front of wannabe coders fleeing dwindling prospects in other industries, certain that the carrot won’t be nearly as big when they all eventually step into a crowded labor market.&lt;/p&gt;&lt;p&gt;The next step is rolling, industry-wide mass layoffs – often obscured under the guise of “stack ranking” or some similar nonsense. Big tech has been callously cutting jobs everywhere, leaving workers out in the cold in batches of thousands or tens of thousands. If you don’t count yourself among them yet, maybe you will soon. What are your prospects for re-hire going to look like if this looming recession materializes in the next few years?&lt;/p&gt;&lt;p&gt;Consider what’s happening now – why do you think tech is driving AI mandates down from the top? Have you been ordered to use an LLM assistant to “help” with your programming? Have you even thought about why the executives would push this crap on you? You’re “training” your replacement. Do you really think that, if LLMs really are going to change the way we code, they &lt;em&gt;aren’t&lt;/em&gt; going to change the way we’re paid for it? Do you think your boss doesn’t see AI as a chance to take $100M off of their payroll expenses?&lt;/p&gt;&lt;p&gt;Aren’t you worried you could get laid off and this junior compsci grad or an H1B takes your place for half your salary? You should be – it’s happening everywhere. What are you going to do about it? Resent the younger generation of programmers just entering the tech workforce? Or the immigrant whose family pooled their resources to send them abroad to study and work? Or maybe you weren’t laid off yet, and you fancy yourself better than the poor saps down the hall who were. Don’t be a sucker – your enemy isn’t in the cubicle next to you, or on the other side of the open office. Your enemy has an office with a door on it.&lt;/p&gt;&lt;p&gt;Listen: a tech union isn’t just about negotiating higher wages and benefits, although that’s definitely on the table. It’s about protecting yourself, and your colleagues, from the relentless campaign against labor that the tech leadership is waging against us. And more than that, it’s about seizing some of the awesome, society-bending power of the tech giants. Look around you and see what destructive ends this power is being applied to. You have your hands at the levers of this power if only you rise together with your peers and make demands.&lt;/p&gt;&lt;p&gt;And if you don’t, you are responsible for what’s going to happen next.&lt;/p&gt;&lt;h2&gt;The imperative to organize is existential&lt;/h2&gt;&lt;p&gt;If global warming is limited to 2°C, here’s what Palo Alto looks like in 2100:&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/palo-alto-1ft.png&quot;&gt;
&lt;figcaption&gt;Map of Palo Alto showing flooding near the coast&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Limiting warming to 2° C requires us to cut global emissions &lt;em&gt;in half&lt;/em&gt; by 2030 – in 5 years – but emissions haven’t even peaked yet. Present-day climate policies are only expected to limit warming to 2.5° to 2.9° C by 2100.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; Here’s Palo Alto in 75 years if we stay our current course:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/palo-alto-3ft.png&quot;&gt;
&lt;figcaption&gt;Map of Palo Alto showing much more extreme flooding&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Here’s the Gulf of Mexico in 75 years:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/gulf-of-mexico.png&quot;&gt;
&lt;figcaption&gt;Gulf of Mexico showing&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;This is what will happen if things don’t improve. Things aren’t improving – they’re getting worse. The US elected an anti-science president who backed out of the Paris agreement, for a start. Your boss is pouring all of our freshwater into datacenters to train these fucking LLMs and &lt;em&gt;expanding&lt;/em&gt; into this exciting new market with millions of tons of emissions as the price of investment. Cryptocurrencies &lt;em&gt;still&lt;/em&gt; account for a full 1% of global emissions. Datacenters as a whole account for 2%. That’s on us – tech workers. That is our fucking responsibility.&lt;/p&gt;&lt;p&gt;Climate change is accelerating, and faster than we thought, and the rich and powerful are making it happen &lt;em&gt;faster&lt;/em&gt;. Climate catastrophe is not in the far future, it’s not our children or our children’s children, it’s &lt;em&gt;us&lt;/em&gt;, it’s &lt;em&gt;already happening&lt;/em&gt;. You and I will live to see dozens of global catastrophes playing out in our lifetimes, with horrifying results. Even if we started a revolution tomorrow and overthrew the ruling class and implemented aggressive climate policies right now we will still watch tens or hundreds of millions die.&lt;/p&gt;&lt;p&gt;Let’s say you are comfortably living outside of these blue areas, and you’ll be sitting pretty when Louisiana or Bruges or Fiji are flooded. Well, 13 million Americans are expected to have to migrate out of flooded areas – and 216 million globally&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; – within 25 to 30 years. That’s just from the direct causes of climate change – as many as 1 billion could be displaced if we account for the ensuing global conflict and civil unrest.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; What do you think will happen to non-coastal cities and states when 4% of the American population is forced to flee their homes? You think you won’t be affected by that? What happens when anywhere from 2.5% to 12% of the Earth’s population becomes refugees?&lt;/p&gt;&lt;p&gt;What are you going to eat? Climate change is going to impact fresh water supplies and reduce the world’s agriculturally productive land. Livestock is expected to be reduced by 7-10% in just 25 years.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; Food prices will skyrocket and people will starve. 7% of all species on Earth may already be extinct because of human activities.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; You think that’s not going to affect you?&lt;/p&gt;&lt;p&gt;The overwhelming majority of the population supports climate action.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fn-7-ref-1&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; The reason it’s not happening is because, under capitalism, capital is power, and the few have it and the many don’t. We live in a global plutocracy.&lt;/p&gt;&lt;p&gt;The plutocracy has an answer to climate change: fascism. When 12% of the world’s population is knocking at the doors of the global north, their answer will be concentration camps and mass murder. &lt;a href=&quot;https://en.wikipedia.org/wiki/U.S._Immigration_and_Customs_Enforcement#ERO_detention_centers&quot; target=&quot;_blank&quot;&gt;They are already working on it today&lt;/a&gt;. When the problem is capitalism, the capitalists will go to any lengths necessary to preserve the institutions that give them power – &lt;a href=&quot;https://www.youtube.com/watch?v=URABscYOjRE&quot; target=&quot;_blank&quot;&gt;they always have&lt;/a&gt;. They have no moral compass or reason besides profit, wealth, and power. The 1% will burn and pillage and murder the 99% without blinking.&lt;/p&gt;&lt;p&gt;They are &lt;em&gt;already&lt;/em&gt; murdering us. 1.2 million Americans are rationing their insulin.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-8&quot; id=&quot;fn-8-ref-1&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; The healthcare industry, organized around the profit motive, murders 68,000 Americans per year.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-9&quot; id=&quot;fn-9-ref-1&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; To the Europeans among my readership, don’t get too comfortable, because I assure you that our leaders are working on destroying our healthcare systems, too.&lt;/p&gt;&lt;p&gt;Someone you love will be laid off, get sick, and die because they can’t afford healthcare. Someone you know, probably many people that you know, will be killed by climate change. It might be someone you love. It might be you.&lt;/p&gt;&lt;p&gt;When you do get laid off mid-recession, your employer replaces you and three of your peers with a fresh bootcamp “graduate” and a GitHub Copilot subscription, and all of the companies you might apply to have done the same… how long can you keep paying rent? What about your friends and family, those who don’t have a cushy tech job or tech worker prospects, what happens when they get laid off or automated away or just priced out of the cost of living? Homelessness is at an all time high and it’s only going to get higher. Being homeless takes 30 years off of your life expectancy.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-10&quot; id=&quot;fn-10-ref-1&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; In the United States, there are 28 vacant homes for every homeless person.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-11&quot; id=&quot;fn-11-ref-1&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Capitalism is going to murder the people you love. Capitalism is going to murder &lt;em&gt;you&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;We need a different answer to the crises that we face. Fortunately, the working class can offer a better solution – one with a long history of success.&lt;/p&gt;&lt;h2&gt;Organizing is the only answer and it will work&lt;/h2&gt;&lt;p&gt;The rich are literally going to kill you and everyone you know and love just because it will make them richer. Because it &lt;em&gt;is&lt;/em&gt; making them richer.&lt;/p&gt;&lt;p&gt;Do you want to do something about any of the real, urgent problems you face? Do you want to make meaningful, rapid progress on climate change, take the catastrophic consequences we are already guaranteed to face in stride, and keep your friends and family safe?&lt;/p&gt;&lt;p&gt;Well, tough shit – you can’t. Don’t tell me you’ll refuse the work, or that it’ll get done anyway without you, or that you can just find another job. They’ll replace you, you won’t find another job, and the world will still burn. You can’t vote your way to a solution, either: elections don’t matter, your vote doesn’t matter, and your voice is worthless to politicians.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-12&quot; id=&quot;fn-12-ref-1&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; Martin Gilens and Benjamin Page demonstrated this most clearly in their 2014 study, “Testing Theories of American Politics: Elites, Interest Groups, and Average Citizens”.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-13&quot; id=&quot;fn-13-ref-1&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Gilens and Page plotted a line chart which shows us the relationship between the odds of a policy proposal being adopted (Y axis) charted against public support for the policy (X axis). If policy adoption was entirely driven by public opinion, we would expect a 45° line (Y=X), where broad public support guarantees adoption and broad public opposition prevents adoption. We could also substitute “public opinion” for the opinions of different subsets of the public to see their relative impact on policy. Here’s what they got:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/policy-vs-opinion.png&quot;&gt;
&lt;figcaption&gt;Two graphs, the first labelled “Average Citizens’ Preferences” and the second “Economic Elites’ Preferences”, showing that the former has little to no correlation with the odds of a policy being adopted, and the latter has a significant impact&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;For most of us, we get a flat line: Y, policy adoption, is completely unrelated to X, public support. Our opinion has no influence whatsoever on policy adoption. Public condemnation or widespread support has the same effect on a policy proposal, i.e. none. But for the wealthy, it’s a different story entirely. I’ve never seen it stated so plainly and clearly: the only thing that matters is money, wealth, and capital. Money is power, and the rich have it and you don’t.&lt;/p&gt;&lt;p&gt;Nevertheless, &lt;strong&gt;you&lt;/strong&gt; must solve these problems. &lt;strong&gt;You&lt;/strong&gt; must participate in finding and implementing solutions. &lt;strong&gt;You&lt;/strong&gt; will be &lt;em&gt;fucked&lt;/em&gt; if you don’t. But it is an unassailable fact that you &lt;em&gt;can’t&lt;/em&gt; solve these problems, because you have no power – at least, not alone.&lt;/p&gt;&lt;p&gt;Together, we &lt;em&gt;do&lt;/em&gt; have power. In fact, we &lt;em&gt;can&lt;/em&gt; fuck with those bastards’ money and they &lt;em&gt;will&lt;/em&gt; step in line if, and only if, we organize. It is the only solution, and it will work.&lt;/p&gt;&lt;p&gt;The ultra-rich possess no morals or ideology or passion or reason. They align with fascists because the fascists promise what they want, namely tax cuts, subsidies, favorable regulation, and cracking the skulls of socialists against the pavement. The rich hoard and pillage and murder with abandon for one reason and one reason only: it’s profitable. The rich always do what makes them richer, and &lt;em&gt;only&lt;/em&gt; what makes them richer. Consequently, you need to make this a losing strategy. You need to make it more profitable to do what you want. To control the rich, you must threaten the only thing they care about.&lt;/p&gt;&lt;p&gt;Strikes are so costly for companies that they will do anything to prevent them – and if they fail to prevent them, then shareholders will pressure them to capitulate if only to stop the hemorrhaging of profit. This threat is so powerful that it doesn’t have to stop at negotiating your salary and benefits. You could demand your employer participate in boycotting Israel. You could demand that your employer stops anti-social lobbying efforts, or even adopts a pro-social lobbying program. You could demand that your CEO cannot support causes that threaten the lives and dignity of their queer or PoC employees. You could demand that they don’t bend the knee to fascists. If you get them where it hurts – their wallet – they will fall in line. They are more afraid of you than we are afraid of them. They are &lt;em&gt;terrified&lt;/em&gt; of us, and it’s time we used that to our advantage.&lt;/p&gt;&lt;p&gt;We know it works because it has always worked. In 2023, United Auto Workers went on strike and most workers won a 25% raise. In February, teachers in Los Angeles went on strike for just 8 days and secured a 19% raise. Nurses in Oregon won a 22% raise, better working schedules, and more this year – and Hawaiian nurses secured an agreement to improve worker/patient ratios in September. Tech workers could take a page out of the Writer’s Guild’s book – in 2023 they secured a prohibition against the use of their work to train AI models and the use of AI to suppress their wages.&lt;/p&gt;&lt;p&gt;Organized labor is powerful and consistently gets concessions from the rich and powerful in a way that no other strategy has ever been able to. It works, and we have a moral obligation to do it. Unions get results.&lt;/p&gt;&lt;h2&gt;How to organize step by step&lt;/h2&gt;&lt;p&gt;I will give you a step-by-step plan for exactly what you need to do to start moving the needle here. The process is as follows:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Building solidarity and community with your peers&lt;/li&gt;&lt;li&gt;Understanding your rights and how to organize safely&lt;/li&gt;&lt;li&gt;Establishing the consensus to unionize, and doing it&lt;/li&gt;&lt;li&gt;Promoting solidarity across tech workplaces and labor as a whole&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Remember that you will not have to do this alone – in fact, that’s the whole point. Step one is building community with your colleagues. Get to know them personally, establish new friendships and grow the friendships you already have. Learn about each other’s wants, needs, passions, and so on, and find ways to support each other. If someone takes a sick day, organize someone to check on them and make them dinner or pick up their kids from school. Organize a board game night at your home with your colleagues, outside of work hours. Make it a regular event!&lt;/p&gt;&lt;p&gt;Talk to your colleagues about work, and your workplace. Tell each other about your salaries and benefits. When you get a raise, don’t be shy, tell your colleagues how much you got and how you negotiated it. Speak positively about each other at performance reviews and save critical feedback for their ears only. Offer each other advice about how to approach their boss to get their needs met, and be each other’s advocate.&lt;/p&gt;&lt;p&gt;Talk about the power you have to work together to accomplish bigger things. Talk about the advantage of collective action. It can start small – perhaps your team collectively refuses to incorporate LLMs into your workflow. Soon enough you and your colleagues will be thinking about unionizing.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Disclaimer: Knowledge about specific processes and legal considerations in this article is US-specific. Your local laws are likely similar, but you should research the differences with your colleagues.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The process of organizing a union in the US is explained step-by-step at &lt;a href=&quot;https://www.workcenter.gov/step-by-step-guide/&quot; target=&quot;_blank&quot;&gt;workcenter.gov&lt;/a&gt;. More detailed resources, including access to union organizers in your neighborhood, are available from the &lt;a href=&quot;https://aflcio.org/formaunion&quot; target=&quot;_blank&quot;&gt;American Federation of Labor and Congress of Industrial Organizations (AFL-CIO)&lt;/a&gt;. But your biggest resources will be people already organizing in the tech sector: in particular you should consult &lt;a href=&quot;https://code-cwa.org/&quot; target=&quot;_blank&quot;&gt;CODE-CWA&lt;/a&gt;, which works with tech workers to provide mentoring and resources on organizing tech workplaces – and has already helped several tech workplaces organize their unions and start making a difference. They’ve got your back.&lt;/p&gt;&lt;p&gt;This is a good time to make sure that you and your colleagues &lt;a href=&quot;https://www.ufcw.org/union-101/know-your-rights/&quot; target=&quot;_blank&quot;&gt;understand your rights&lt;/a&gt;. First of all, you would be wise to pool your resources and hire the attention of a lawyer specializing in labor – consult your local bar association to find one (it’s easy, just google it and they’ll have a web thing). Definitely reach out to AFL-CIO and CODE-CWA to meet experienced union organizers who can help you.&lt;/p&gt;&lt;p&gt;You cannot be lawfully fired or punished for discussing unions, workplace conditions, or your compensation and benefits, with your colleagues. You cannot be punished for distributing literature in support of your cause, especially if you do it off-site (even just outside of the front door). Be careful not to make careless remarks about your boss’s appearance, complain about the quality of your company’s products, make disparaging comments about clients or customers, etc – don’t give them an easy excuse. Hold meetings and discussions outside of work if necessary, and perform your duties as you normally would while organizing.&lt;/p&gt;&lt;p&gt;Once you start getting serious about organizing, your boss will start to work against you, but know that they cannot stop you. Nevertheless, you and/or some of your colleagues may run the risk of unlawful retaliation or termination for organizing – this is why you should have a lawyer on retainer. This is also why it’s important to establish systems of mutual aid, so that if one of your colleagues gets into trouble you can lean on each other to keep supporting your families. And, importantly, remember that HR works for the company, not for you. HR are the front lines that are going to execute the unionbusting mandates from above.&lt;/p&gt;&lt;p&gt;Once you have a consensus among your colleagues to organize – which you will know because they will have signed union cards – you can approach your employer to ask them to voluntarily recognize the union. If they agree to opening an organized dialogue amicably, you do so. If not, you will reach out to the National Labor Relations Board (&lt;a href=&quot;https://www.nlrb.gov/&quot; target=&quot;_blank&quot;&gt;NLRB&lt;/a&gt;) to organize a vote to unionize. Only organize a vote that you know you will win. Once your workplace votes to unionize, your employer is obligated to negotiate with you in good faith. Start making collective decisions about what you want from your employer and bring them to the table.&lt;/p&gt;&lt;p&gt;In this process, you will have established a relationship with more experienced union organizers who will continue to help you with conducting your union’s affairs and start getting results. The next step is to make yourself available for this purpose to the next tech workplace that wants to unionize: to share what you’ve learned and support the rest of the industry in solidarity. Talk to your friends across the industry and build solidarity and power in mass.&lt;/p&gt;&lt;h3&gt;Prepare for the general strike on May 1st, 2028&lt;/h3&gt;&lt;p&gt;The call has gone out: on Labor Day, 2028 – just under three years from now – &lt;a href=&quot;https://www.thenation.com/article/activism/general-strike-2028-unions-labor-movement/&quot; target=&quot;_blank&quot;&gt;there will be a general strike&lt;/a&gt; in the United States. The United Auto Workers union, one of the largest in the United States, has arranged for their collective bargaining agreements to end on this date, and has called for other unions to do the same across all industries. The American Federation of Teachers and its 1.2 million members are on board, and other unions are sure to follow. Your new union should be among them.&lt;/p&gt;&lt;p&gt;This is how we collectively challenge not just our own employers, but our political institutions as a whole. This is how we turn this nightmare around.&lt;/p&gt;&lt;p&gt;A mass strike is a difficult thing to organize. It is certain to be met with large-scale, coordinated, and well-funded propaganda and retaliation from the business and political spheres. Moreover, a mass strike depends on careful planning and mass mutual aid. We need to be prepared to support each other to get it done, and to plan and organize seriously. When you and your colleagues get organized, discuss this strike amongst yourselves and be prepared to join in solidarity with the rest of the 99% around the country and the world at large.&lt;/p&gt;&lt;p&gt;To commit yourselves to participate or get involved in the planning of the grassroots movement, see &lt;a href=&quot;https://generalstrikeus.com/&quot; target=&quot;_blank&quot;&gt;generalstrikeus.com&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Join unitelabor.dev&lt;/h2&gt;&lt;p&gt;I’ve set up a &lt;a href=&quot;https://www.discourse.org/&quot; target=&quot;_blank&quot;&gt;Discourse&lt;/a&gt; instance for discussion, organizing, Q&amp;A, and solidarity among tech workers at &lt;a href=&quot;https://unitelabor.dev&quot; target=&quot;_blank&quot;&gt;unitelabor.dev&lt;/a&gt;. Please check it out!&lt;/p&gt;&lt;p&gt;If you have any questions or feedback on this article, please post about it there.&lt;/p&gt;&lt;h2&gt;Unionize or die&lt;/h2&gt;&lt;p&gt;You must organize, and you must start now, or the worst will come to pass. Fight like your life depends on it, beause it does. It has never been more urgent. The tech industry needs to stop fucking around and get organized.&lt;/p&gt;&lt;p&gt;We are powerful together. We can change things, and we must. Spread the word, in your workplace and with your friends and online. On the latter, be ready to fight just to speak – especially in our online spaces owned and controlled by the rich (&lt;em&gt;ahem&lt;/em&gt; – YCombinator, Reddit, Twitter – etc). But fight all the same, and don’t stop fighting until we’re done.&lt;/p&gt;&lt;p&gt;We can do it, together.&lt;/p&gt;&lt;h3&gt;Resources&lt;/h3&gt;&lt;p&gt;Tech-specific:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://unitelabor.dev&quot; target=&quot;_blank&quot;&gt;unitelabor.dev&lt;/a&gt; (new)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://techworkerscoalition.org/&quot; target=&quot;_blank&quot;&gt;Tech Workers Coalition&lt;/a&gt; (Global)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://code-cwa.org/&quot; target=&quot;_blank&quot;&gt;CODE-CWA&lt;/a&gt; (US)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://utaw.tech/&quot; target=&quot;_blank&quot;&gt;UTAW&lt;/a&gt; (UK)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://prospect.org.uk/tech-workers/&quot; target=&quot;_blank&quot;&gt;Prospect&lt;/a&gt; (UK)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.alphabetworkersunion.org/&quot; target=&quot;_blank&quot;&gt;Alphabet Workers Union&lt;/a&gt; (Google)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;General:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.iww.org/&quot; target=&quot;_blank&quot;&gt;Industrial Workers of the World&lt;/a&gt; (Global)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.iclcit.org/&quot; target=&quot;_blank&quot;&gt;International Confederation of Labor&lt;/a&gt; (Global)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://aflcio.org/formaunion&quot; target=&quot;_blank&quot;&gt;AFL-CIO&lt;/a&gt; (US)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.workcenter.gov/step-by-step-guide/&quot; target=&quot;_blank&quot;&gt;workcenter.gov&lt;/a&gt; (US)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://generalstrikeus.com/&quot; target=&quot;_blank&quot;&gt;generalstrikeus.com&lt;/a&gt; (US)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://workerorganizing.org/&quot; target=&quot;_blank&quot;&gt;Emergency Workplace Organizing Committee&lt;/a&gt; (US)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.labornotes.org/&quot; target=&quot;_blank&quot;&gt;Labor Notes&lt;/a&gt; (US)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.organisenow.org.uk/&quot; target=&quot;_blank&quot;&gt;Organize Now!&lt;/a&gt; (UK)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Send me more&lt;/a&gt; resources to add here!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Unionize-or-die/</link>
        
        <pubDate>Mon, 09 Jun 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Unionize-or-die/</guid>
      </item>
    
      <item>
        
        
          <title>The British Airways position on various border disputes</title>
          <description>
            &lt;p&gt;My spouse and I are on vacation in Japan, spending half our time seeing the sights and the other half working remotely and enjoying the experience of living in a different place for a while. To get here, we flew on British Airways from London to Tokyo, and I entertained myself on the long flight by browsing the interactive flight map on the back of my neighbor’s seat and trying to figure out how the poor developer who implemented this map solved the thorny problems that displaying a world map implies.&lt;/p&gt;&lt;p&gt;I began my survey by poking through the whole interface of this little in-seat entertainment system&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; to see if I can find out anything about who made it or how it works – I was particularly curious to find a screen listing open source licenses that such such devices often disclose. To my dismay I found nothing at all – no information about who made it or what’s inside. I imagine that there &lt;em&gt;must&lt;/em&gt; be some open source software in that thing, but I didn’t find any licenses or copyright statements.&lt;/p&gt;&lt;p&gt;When I turned my attention to the map itself, I did find one copyright statement, the only one I could find in the whole UI. If you zoom in enough, it switches from a satellite view to a street view showing the OpenStreetMap copyright line:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/ba-map-osm-copyright.jpg&quot; alt=&quot;Picture of the display showing &apos;Street Maps: (c) OpenStreetMap contributors&apos;&quot;&gt;
&lt;figcaption&gt;Note that all of the pictures in this article were taken by pointing my smartphone camera at the screen from an awkward angle and fine-tune your expectations accordingly. I don’t have pictures to support every border claim documented in this article, but I did take notes during the flight.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Given that British Airways is the proud flag carrier of the United Kingdom I assume that this is indeed the only off-the-shelf copyrighted material included in this display, and everything else was developed in-house without relying on any open source software that might require a disclosure of license and copyright details. For similar reasons I am going to assume that all of the borders shown in this map are reflective of the official opinion of British Airways on various international disputes.&lt;/p&gt;&lt;p&gt;As I briefly mentioned a moment ago, this map has two views: satellite photography and a very basic street view. Your plane and its route are shown in real-time, and you can touch the screen to pan and zoom the map anywhere you like. You can also rotate the map and change the angle in “3D” if you have enough patience to use complex multitouch gestures on the cheapest touch panel they could find.&lt;/p&gt;&lt;p&gt;The street view is very sparse and only appears when you’re pretty far zoomed in, so it was mostly useless for this investigation. The satellite map, thankfully, includes labels: cities, country names, points of interest, and, importantly, national borders. The latter are very faint, however. Here’s an illustrative example:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/ba-borders-illustration.jpg&quot; alt=&quot;A picture of the screen showing the area near the Caucasus mountains with the plane overflying the Caspian sea&quot;&gt;&lt;/p&gt;&lt;p&gt;We also have our first peek at a border dispute here: look closely between the “Georgia” and “Caucasus Mountains” labels. This ever-so-faint dotted line shows what I believe is the Russian-occupied territory of South Ossetia in Georgia. Disputes implicating Russia are not universally denoted as such – I took a peek at the border with Ukraine and found that Ukraine is shown as whole and undisputed, with its (undotted) border showing Donetsk, Luhansk, and Crimea entirely within Ukraine’s borders.&lt;/p&gt;&lt;p&gt;Of course, I didn’t start at Russian border disputes when I went looking for trouble. I went directly to Palestine. Or rather, I went to Israel, because Palestine doesn’t exist on this map:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/ba-israel-palestine.jpg&quot; alt=&quot;Picture of the screen showing Israel&quot;&gt;&lt;/p&gt;&lt;p&gt;I squinted and looked very closely at the screen and I’m &lt;em&gt;fairly&lt;/em&gt; certain that both the West Bank and Gaza are outlined in these dotted lines using the borders defined by the 1949 armistice. If you zoom in a bit more to the street view, you can see labels like “West Bank” and the “Area A”, “Area B” labels of the Oslo Accords:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/ba-west-bank.jpg&quot; alt=&quot;Picture of the street map zoomed in on Ramallah&quot;&gt;&lt;/p&gt;&lt;p&gt;Given that this is British Airways, part of me was surprised not to see the whole area simply labelled Mandatory Palestine, but it is interesting to know that British Airways officially supports the Oslo Accords.&lt;/p&gt;&lt;p&gt;Heading south, let’s take a look at the situation in Sudan:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/ba-sudan.jpg&quot; alt=&quot;Picture of the satellite map over Sudan&quot;&gt;&lt;/p&gt;&lt;p&gt;This one is interesting – three areas within South Sudan’s claimed borders are disputed, and the map only shows two with these dotted lines. The border dispute with Sudan in the northeast is resolved in South Sudan’s favor. Another case where BA takes a stand is Guyana, which has an ongoing dispute with Venezuela – but the map &lt;em&gt;only&lt;/em&gt; shows Guyana’s claim, albeit with a dotted line, rather than the usual approach of drawing both claims with dotted lines.&lt;/p&gt;&lt;p&gt;Next, I turned my attention to Taiwan:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/ba-taiwan.jpg&quot; alt=&quot;Picture of the satellite map over eastern China and Taiwan&quot;&gt;&lt;/p&gt;&lt;p&gt;The cities of Taipei and Kaohsiung are labelled, but the island as a whole was not labelled “Taiwan”. I zoomed and panned and 3D-zoomed the map all over the place but was unable to get a “Taiwan” label to appear. I also zoomed into the OSM-provided street map and panned that around but couldn’t find “Taiwan” anywhere, either.&lt;/p&gt;&lt;p&gt;The last picture I took is of the Kashmir area:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/ba-kashmir.jpg&quot; alt=&quot;Picture of the satellite map showing the Kashmir region&quot;&gt;&lt;/p&gt;&lt;p&gt;I find these faint borders difficult to interpret and I admit to not being very familiar with this conflict, but perhaps someone in the know with the patience to look more closely will &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email me&lt;/a&gt; their understanding of the official British Airways position on the Kashmir conflict (here’s the &lt;a href=&quot;https://redacted.moe/f/70bc3338.jpg&quot; target=&quot;_blank&quot;&gt;full sized picture&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;Here are some other details I noted as I browsed the map:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The Hala’ib Triangle and Bir Tawil are shown with dotted lines&lt;/li&gt;&lt;li&gt;The Gulf of Mexico is labelled as such&lt;/li&gt;&lt;li&gt;Antarctica has no labelled borders or settlements&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;After this thrilling survey of the official political positions of British Airways, I spent the rest of the flight reading books or trying to sleep.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/BA-on-border-disputes/</link>
        
        <pubDate>Mon, 05 May 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/BA-on-border-disputes/</guid>
      </item>
    
      <item>
        
        
          <title>Resistance from the tech sector</title>
          <description>
            &lt;p&gt;As of late, most of us have been reading the news with a sense of anxious trepidation. At least, those of us who read from a position of relative comfort and privilege. Many more read the news with fear. Some of us are already no longer in a position to read the news at all, having become the unfortunate subjects of the news. Fascism is on the rise worldwide and in the United States the news is particularly alarming. The time has arrived to act.&lt;/p&gt;&lt;p&gt;The enemy wants you to be overwhelmed and depressed, to feel like the situation is out of your control. Propaganda is as effective on me as it is on you, and in my own home the despair and helplessness the enemy aims to engineer in us often prevails in my own life. &lt;a href=&quot;https://archive.org/details/0022_Dont_Be_a_Sucker_22_33_53_00&quot; target=&quot;_blank&quot;&gt;We mustn’t fall for this gambit&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;When it comes to resistance, I don’t have all of the answers, and I cannot present a holistic strategy for effective resistance.  Nevertheless, I have put some thought towards how someone in my position, or in my community, can effectively apply ourselves towards resistance.&lt;/p&gt;&lt;p&gt;The fact of the matter is that the tech sector is extraordinarily important in enabling and facilitating the destructive tide of contemporary fascism’s ascent to power. The United States is embracing a technocratic fascism at the hands of Elon Musk and his techno-fetishist “Department of Government Efficiency”. Using memes to mobilize the terminally online neo-right, and “digitizing” and “modernizing” government institutions with the dazzling miracles of modern technology, the strategy puts tech, in its mythologized form – prophesied, even, through the medium of science fiction – at the center of a revolution of authoritarian hate.&lt;/p&gt;&lt;p&gt;And still, this glitz and razzle dazzle act obscures the more profound and dangerous applications of tech hegemony to fascism. Allow me to introduce public enemy number one: Palantir. Under the direction of neo-fascist Peter Thiel and in collaboration with &lt;abbr title=&quot;Immigrations and Customs Enforcement&quot;&gt;ICE&lt;/abbr&gt;, Palantir is applying the innovations of the last few decades of surveillance capitalism to implementing a &lt;a href=&quot;https://www.404media.co/ice-plans-central-database-of-health-labor-housing-agency-data-to-find-targets/&quot; target=&quot;_blank&quot;&gt;database of undesirables&lt;/a&gt; the Nazis could have never dreamed of. Where DOGE is hilariously tragic, Palantir is nightmarishly effective.&lt;/p&gt;&lt;p&gt;It’s clear that the regime will be digital. The through line is tech – and the tech sector depends on tech workers. That’s us. This puts us in a position to act, and compels us to act. But then, what should we do?&lt;/p&gt;&lt;p&gt;If there’s one thing I want you to take away from this article, something to write on your mirror and repeat aloud to yourself every day, it’s this: there’s safety in numbers. It is of the utmost importance that we dispense with American individualism and join hands with our allies to resist as one. Find your people in your local community, and especially in your workplace, who you can trust and who believe in what’s right and that you can depend on for support. It’s easier if you’re not going it alone. Talk to your colleagues about your worries and lean on them to ease your fears, and allow them to lean on you in turn.&lt;/p&gt;&lt;p&gt;One of the most important actions you can take is to unionize your workplace. We are long overdue for a tech workers union. If tech workers unionize then we can compel our employers – this regime’s instruments of fascist power – to resist also. If you’re at the bottom looking up at your boss’s boss’s boss cozying up with fascists, know that with a union you can pull the foundations of his power out from beneath him.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.gutenberg.org/ebooks/26184&quot; target=&quot;_blank&quot;&gt;More direct means&lt;/a&gt; of resistance are also possible, especially for the privileged and highly paid employees of big tech. Maneuver yourself towards the levers of power. At your current job, find your way onto the teams implementing the technology that enables authoritarianism, and fuck it up. Drop the database by “mistake”. Overlook bugs. Be confidently wrong in code reviews and meetings. Apply for a job at Palantir, and be incompetent at it. Make yourself a single point of failure, then fail. Remember too that plausible deniability is key – make them work to figure out that &lt;em&gt;you&lt;/em&gt; are the problem.&lt;/p&gt;&lt;p&gt;This sort of action is scary and much riskier than you’re probably immediately comfortable with. Inaction carries risks also. Only you are able to decide what your tolerance for risk is, and what kind of action that calls for. If your appetite for risk doesn’t permit sabotage, you could simply refuse to work on projects that aren’t right. Supporting others is essential resistance, too – be there for your friends, especially those more vulnerable than yourself, and support the people who engage in direct resistance. You didn’t see nuffin, right? If your allies get fired for fucking up an important digital surveillance project – you’ll have a glowing reference for them when they apply for Palantir, right?&lt;/p&gt;&lt;p&gt;Big tech has become the problem, and it’s time for tech workers to be a part of the solution. If this scares you – and it should – I get it. I’m scared, too. It’s okay for it to be scary. It’s okay for you not to do anything about it right now. All you have to do right now is be there for your friends and loved ones, and answer this question: where will you draw the line?&lt;/p&gt;&lt;p&gt;Remember your answer, and if and when it comes to pass… you will know when to act. Don’t let them shift your private goalposts until the frog is well and truly boiled to death.&lt;/p&gt;&lt;p&gt;Hang in there.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Tech-sector-restistance/</link>
        
        <pubDate>Sun, 20 Apr 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Tech-sector-restistance/</guid>
      </item>
    
      <item>
        
        
          <title>A Firefox addon for putting prices into perspective</title>
          <description>
            &lt;p&gt;I had a fun idea for a small project this weekend, and so I quickly put it together over the couple of days. The result is &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/price-perspective/&quot; target=&quot;_blank&quot;&gt;Price Perspective&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Humor me: have you ever bought something, considered the price, and wondered how that price would look to someone else? Someone in the developing world, or a billionaire, or just your friend in Australia? In other words, can we develop an intuition for &lt;a href=&quot;https://en.wikipedia.org/wiki/Purchasing_power&quot; target=&quot;_blank&quot;&gt;purchasing power&lt;/a&gt;?&lt;/p&gt;&lt;p&gt;The Price Perspective add-on answers these questions. Let’s consider an example: my income is sufficient to buy myself a delivery pizza for dinner without a second thought. How much work does it take for someone in Afghanistan to buy the same pizza? I can fire up Price Perspective to check:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://redacted.moe/f/7e13a09b.png&quot;&gt;&lt;/p&gt;&lt;p&gt;The results are pretty shocking.&lt;/p&gt;&lt;p&gt;How about another example: say I’m looking to buy a house in the Netherlands. I fire up funda.nl and look at a few places in Amsterdam. After a few minutes wondering if I’ll ever be in an economic position to actually &lt;em&gt;afford&lt;/em&gt; any of these homes (and speculating on if that day will come before or after I have spent this much money on rent over my lifetime), I wonder what these prices look like from the other side. Let’s see what it’d take for the Zuck to buy this apartment I fancy:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://redacted.moe/f/37ddbe71.png&quot;&gt;&lt;/p&gt;&lt;p&gt;Well… that’s depressing. Let’s experiment with Price Perspective to see what it would take to make a dent in Zuck’s wallet. Let’s add some zeroes.&lt;/p&gt;&lt;p&gt;&lt;video src=&quot;https://redacted.moe/f/2752d5bc.webm&quot; autoplay loop muted controls&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;So, Zuckerberg over-bidding this apartment to the tune of €6.5B would cost him a proportion of his annual income which is comparable to me buying it for €5,000.&lt;/p&gt;&lt;p&gt;How about the reverse? How long would I have to work to buy, say, Jeff Bezos’s new mansion?&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://redacted.moe/f/608ace06.png&quot;&gt;&lt;/p&gt;&lt;p&gt;Yep. That level of wealth inequality is a sign of a totally normal, healthy, well-functioning society.&lt;/p&gt;&lt;p&gt;Curious to try it out for yourself? Get Price Perspective from &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/price-perspective/&quot; target=&quot;_blank&quot;&gt;addons.mozilla.org&lt;/a&gt;, tell it where you live and how much money you make in a year, and develop your own sense of perspective.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Price-perspective/</link>
        
        <pubDate>Fri, 04 Apr 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Price-perspective/</guid>
      </item>
    
      <item>
        
        
          <title>Using linkhut to signal-boost my bookmarks</title>
          <description>
            &lt;hr&gt;&lt;p&gt;&lt;strong&gt;Notice&lt;/strong&gt;: linkhut has started to use generative AI tools upstream. Consequently, I have withdrawn my endorsement of the project.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;It must have been at least a year ago that I first noticed &lt;a href=&quot;https://linkhut.org/&quot; target=&quot;_blank&quot;&gt;linkhut&lt;/a&gt;, and its flagship instance at &lt;a href=&quot;https://ln.ht&quot; target=&quot;_blank&quot;&gt;ln.ht&lt;/a&gt;, appear on SourceHut, where it immediately caught my attention for its good taste in inspirations. Once upon a time, I had a &lt;a href=&quot;https://pinboard.in/&quot; target=&quot;_blank&quot;&gt;Pinboard&lt;/a&gt; account, which is a similar concept, but I never used it for anything in the end. When I saw linkhut I had a similar experience: I signed up and played with it for a few minutes before moving on.&lt;/p&gt;&lt;p&gt;I’ve been rethinking my relationship social media lately, as some may have inferred from my unannounced disappearance from Mastodon.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; While reflecting on this again recently, in a stroke of belated inspiration I suddenly appreciated the appeal of tools like linkhut, especially alongside RSS feeds – signal-boosting stuff I read and found interesting.&lt;/p&gt;&lt;p&gt;The appeal of this reminds me of one of the major appeals of SoundCloud to me, back when I used it circa… 2013? That is: I could listen to the music that artists I liked were listening to, and that was &lt;em&gt;amazing&lt;/em&gt; for discovering new music. Similarly, for those of you who enjoy my blog posts, and want to read the stuff I like reading, check out my &lt;a href=&quot;https://ln.ht/~ddevault&quot; target=&quot;_blank&quot;&gt;linkhut feed&lt;/a&gt;. You can even &lt;a href=&quot;https://ln.ht/_/feed/~ddevault&quot; target=&quot;_blank&quot;&gt;subscribe to its RSS feed&lt;/a&gt; if you like. There isn’t much there today, but I will be filling it up with interesting articles I see and projects I find online.&lt;/p&gt;&lt;p&gt;I want to read &lt;em&gt;your&lt;/em&gt; linkhut feed, too, but it’s pretty quiet there at the moment. If you find the idea interesting, sign up for an account or set up your own instance and start bookmarking stuff – and &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email me&lt;/a&gt; your feed so I can find some good stuff to subscribe to in my own feed reader.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Using-linkhut/</link>
        
        <pubDate>Thu, 27 Mar 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Using-linkhut/</guid>
      </item>
    
      <item>
        
        
          <title>Please stop externalizing your costs directly into my face</title>
          <description>
            &lt;p&gt;&lt;em&gt;This blog post is expressing personal experiences and opinions and doesn’t reflect any official policies of SourceHut.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Over the past few months, instead of working on our priorities at SourceHut, I have spent anywhere from 20-100% of my time in any given week mitigating hyper-aggressive LLM crawlers at scale. This isn’t the first time SourceHut has been at the wrong end of some malicious bullshit or paid someone else’s externalized costs – every couple of years someone invents a new way of ruining my day.&lt;/p&gt;&lt;p&gt;Four years ago, we decided to &lt;a href=&quot;https://man.sr.ht/ops/builds.sr.ht-migration.md&quot; target=&quot;_blank&quot;&gt;require payment to use our CI services&lt;/a&gt; because it was being abused to mine cryptocurrency. We alternated between periods of designing and deploying tools to curb this abuse and periods of near-complete outage when they adapted to our mitigations and saturated all of our compute with miners seeking a profit. It was bad enough having to beg my friends and family to avoid “investing” in the scam without having the scam break into my business and trash the place every day.&lt;/p&gt;&lt;p&gt;Two years ago, we threatened to &lt;a href=&quot;https://sourcehut.org/blog/2023-01-09-gomodulemirror/&quot; target=&quot;_blank&quot;&gt;blacklist the Go module mirror&lt;/a&gt; because for some reason the Go team thinks that running terabytes of git clones all day, every day for every Go project on git.sr.ht is cheaper than maintaining any state or using webhooks or coordinating the work between instances or even just designing a module system that doesn’t require Google to DoS git forges whose entire annual budgets are considerably smaller than a single Google engineer’s salary.&lt;/p&gt;&lt;p&gt;Now it’s LLMs. If you think these crawlers respect robots.txt then you are several assumptions of good faith removed from reality. These bots crawl everything they can find, robots.txt be damned, including expensive endpoints like git blame, every page of every git log, and every commit in every repo, and they do so using random User-Agents that overlap with end-users and come from tens of thousands of IP addresses – mostly residential, in unrelated subnets, each one making no more than one HTTP request over any time period we tried to measure – actively and maliciously adapting and blending in with end-user traffic and avoiding attempts to characterize their behavior or block their traffic.&lt;/p&gt;&lt;p&gt;We are experiencing dozens of brief outages per week, and I have to review our mitigations several times per day to keep that number from getting any higher. When I do have time to work on something else, often I have to drop it when all of our alarms go off because our current set of mitigations stopped working. Several high-priority tasks at SourceHut have been delayed weeks or even months because we keep being interrupted to deal with these bots, and many users have been negatively affected because our mitigations can’t always reliably distinguish users from bots.&lt;/p&gt;&lt;p&gt;All of my sysadmin friends are dealing with the same problems. I was asking one of them for feedback on a draft of this article and our discussion was interrupted to go deal with a new wave of LLM bots on their own server. Every time I sit down for beers or dinner or to socialize with my sysadmin friends it’s not long before we’re complaining about the bots and asking if the other has cracked the code to getting rid of them once and for all. The desperation in these conversations is palpable.&lt;/p&gt;&lt;p&gt;Whether it’s cryptocurrency scammers mining with FOSS compute resources or Google engineers too lazy to design their software properly or Silicon Valley ripping off all the data they can get their hands on at everyone else’s expense… I am sick and tired of having all of these costs externalized directly into my fucking face. Do something productive for society or get the hell away from my servers. Put all of those billions and billions of dollars towards the common good before sysadmins collectively start a revolution to do it for you.&lt;/p&gt;&lt;p&gt;Please stop legitimizing LLMs or AI image generators or GitHub Copilot or any of this garbage. I am begging you to stop using them, stop talking about them, stop making new ones, just &lt;em&gt;stop&lt;/em&gt;. If blasting CO&lt;sub&gt;2&lt;/sub&gt; into the air and ruining all of our freshwater and traumatizing cheap laborers and making every sysadmin you know miserable and ripping off code and books and art at scale and ruining our fucking democracy isn’t enough for you to leave this shit alone, what is?&lt;/p&gt;&lt;p&gt;If you personally work on developing LLMs et al, know this: I will never work with you again, and I will remember which side you picked when the bubble bursts.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Stop-externalizing-your-costs-on-me/</link>
        
        <pubDate>Mon, 17 Mar 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Stop-externalizing-your-costs-on-me/</guid>
      </item>
    
      <item>
        
        
          <title>A holistic perspective on intellectual property, part 1</title>
          <description>
            &lt;p&gt;I’d like to write about intellectual property in depth, in this first of a series of blog posts on the subject. I’m not a philosopher, but philosophy is the basis of reasonable politics so buckle up for a healthy Friday afternoon serving of it.&lt;/p&gt;&lt;p&gt;To understand intellectual property, we must first establish at least a shallow understanding of property generally. What is property?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; An incomplete answer might state that a material object I have power over is my property. An apple I have held in my hand is mine, insofar as nothing prevents me from using it (and, in the process, destroying it), or giving it away, or planting it in the ground. However, you might not agree that this apple is necessarily &lt;em&gt;mine&lt;/em&gt; if I took it from a fruit stand without permission. This act is called “theft” — one of many possible transgressions upon property.&lt;/p&gt;&lt;p&gt;It is important to note that the very possibility that one could illicitly assume possession of an object is a strong indication that “property” is a social convention, rather than a law of nature; one cannot defy the law of gravity in the same way as one can defy property. And, given that, we could try to imagine other social conventions to govern the use of &lt;em&gt;things&lt;/em&gt; in a society. If we come up with an idea we like, and we’re in a radical mood, we could even challenge the notion of property in society at large and seek to implement a different social convention.&lt;/p&gt;&lt;p&gt;As it stands today, the social convention tells us property is a &lt;em&gt;thing&lt;/em&gt; which has an “owner”, or owners, to whom society confers certain rights with respect to the thing in question. That may include, for example, the right to use it, to destroy it, to exclude others from using it, to sell it, or give it away, and so on. Property is this special idea society uses to grant you the authority to use a bunch of verbs with respect to a thing. However, being a social convention, nothing prevents me from using any of these verbs on something society does not recognize as my property, e.g. by &lt;abbr title=&apos;&quot;I have a bridge to sell you&quot; is an English-language idiom that refers to selling something you do not own to a gullible person.&apos;&gt; selling you this bridge&lt;/abbr&gt;. This is why the social convention must be &lt;strong&gt;enforced&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;And how is it enforced? We could enforce property rights with shame: stealing can put a stain on one’s reputation, and this shame may pose an impediment to one’s social needs and desires, and as such theft is discouraged. We can also use guilt: if you steal something, but don’t get caught, you could end up remorseful without anyone to shame you for it, particularly with respect to the harm done to the person who suffered a loss of property as a result. Ultimately, in modern society the social convention of property is enforced with, well, force. If you steal something, society has appointed someone with a gun to track you down, restrain you, and eventually lock you up in a miserable room with bars on the windows.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;I’d like to take a moment here to acknowledge the hubris of property: we see the bounty of the natural world and impose upon it these imagined rights and privileges, divvy it up and hand it out and hoard it, and resort to cruelty if anyone steps out of line. Indeed this may be justifiable if the system of private property is sufficiently beneficial to society, and the notion of property is so deeply ingrained into our system that it feels normal and unremarkable. It’s worth remembering that it has trade-offs, that we made the whole thing up, and that we can make up something else with different trade-offs. That being said, I’m personally fond of most of my personal property and I’d like to keep enjoying most of my property rights as such, so take from that what you will.&lt;/em&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;One way we can justify property rights is by using them as a tool for managing &lt;em&gt;scarcity&lt;/em&gt;. If demand for coffee exceeds the supply of coffee beans, a scarcity exists, meaning that not everyone who wants to have coffee gets to have some. But, we still want to enjoy scarce things. Perhaps someone who foregoes coffee will enjoy some other scarce resource, such as tea — then everyone can benefit in some part from some access to scarce resources. I suppose that the social convention of property can derive some natural legitimacy from the fact that some resources are scarce.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; In this sense, private property relates to the problem of distribution.&lt;/p&gt;&lt;p&gt;But a naive solution to distribution has flaws. For example, what of hoarding? Are property rights legitimate when someone takes more than they need or intend to use? This behavior could be motivated by an antagonistic relationship with society at large, such as as a means of driving up prices for private profit; such behavior could be considered anti-social and thus a violation of the social convention as such.&lt;/p&gt;&lt;p&gt;Moreover, property which is destroyed by its use, such as coffee, is one matter, but further questions are raised when we consider durable goods, such as a screwdriver. The screwdriver in my shed spends the vast majority of its time out of use. Is it just for me to assert property rights over my screwdriver when I am not using it? To what extent is the scarcity of screwdrivers &lt;em&gt;necessary&lt;/em&gt;? Screwdrivers are not fundamentally scarce, given that the supply of idle screwdrivers far outpaces the demand for screwdriver use, but our modern conception of property has the unintended consequence of creating scarcity where there is none by denying the use of idle screwdrivers where they are needed.&lt;/p&gt;&lt;p&gt;Let’s try to generalize our understanding of property, working our way towards “intellectual property” one step at a time. To begin with, what happens if we expand our understanding of property to include immaterial things? Consider domain names as a kind of property. In theory, domain names are abundant, but some names are more desirable than others. We assert property rights over them, in particular the right to use a name and exclude others from using it, or to derive a profit from exclusive use of a desirable name.&lt;/p&gt;&lt;p&gt;But a domain name doesn’t really exist per-se: it’s just an entry in a ledger. The electric charge on the hard drives in your nearest DNS server’s database exist, but the domain name it represents doesn’t exist in quite the same sense as the electrons do: it’s immaterial. Is applying our conception of property to these immaterial things justifiable?&lt;/p&gt;&lt;p&gt;We can start answering this question by acknowledging that property rights are &lt;em&gt;useful&lt;/em&gt; for domain names, in that this gives domain names desirable properties that serve productive ends in society. For example, exclusive control over a domain name allows a sense of authenticity to emerge from its use, so that you understand that pointing your browser to drewdevault.com will return the content that the person, Drew DeVault, wrote for you. We should also acknowledge that there are negative side-effects of asserting property rights over domains, such as domain squatting, extortionate pricing for “premium” domain names, and the advantage one party has over another if they possess a desirable name by mere fact of that possession, irrespective of merit.&lt;/p&gt;&lt;p&gt;On the balance of things, if we concede the legitimacy of personal property&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; I find it relatively easy to concede the legitimacy of this sort of property, too.&lt;/p&gt;&lt;p&gt;The next step is to consider if we can generalize property rights to govern immaterial, non-finite things, like a story. A book, its paper and bindings and ink, is a material, finite resource, and can be thought of in terms that apply to material property. But what of the words formed by the ink? They can be trivially copied with a pen and paper, or transformed into a new medium by reading it aloud to an audience, and these processes do not infringe on the material property rights associated with the book. This process cannot be thought of as stealing, as the person who possesses a copy of the book is not asserting property rights over the original. In our current intellectual property regime, this person is transgressing via use of the &lt;em&gt;idea&lt;/em&gt;, the &lt;em&gt;intellectual&lt;/em&gt; property — the &lt;em&gt;thing&lt;/em&gt; in the abstract space occupied by the story itself. Is that, too, a just extension of our notion of property?&lt;/p&gt;&lt;p&gt;Imagine with me the relationship one has with one’s property, independent of the social constructs around property. With respect to material property, a relationship of possession exists: I physically possess a thing, and I have the ability to make use of it through my possession of it. If someone else were to deny me access to this thing, they would have to resort to force, and I would have to resort to force should I resist their efforts.&lt;/p&gt;&lt;p&gt;Our relationship with intellectual property is much different. An idea cannot be withheld or seized by force. Instead, our relationship to intellectual property is defined by our &lt;em&gt;history&lt;/em&gt; with respect to an idea. In the case of material property, the ground truth is that I keep it locked in my home to deny others access to it, and the social construct formalizes this relationship. With respect to intellectual property, such as the story in a book, the ground truth is that, sometime in the past, I imagined it and wrote it down. The social construct of intellectual property invents an imagined relationship of possession, modelled after our relationship with material property.&lt;/p&gt;&lt;p&gt;Why?&lt;/p&gt;&lt;p&gt;The resource with the greatest and most fundamental scarcity is our time,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; and as a consequence the labor which goes into making something is of profound importance. Marx famously argued for a “labor theory of value”, which tells us that the value inherent in a good or service is in the labor which is required to provide it. I think he was on to something!&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; Intellectual property is not scarce, nor can it be possessed, but it does have &lt;em&gt;value&lt;/em&gt;, and that value could ultimately be derived from the labor which produced it.&lt;/p&gt;&lt;p&gt;The social justification for intellectual property as a legal concept is rooted in the value of this labor. We recognize that intellectual labor is valuable, and produces an artifact — e.g. a story — which is valuable, but is not scarce. A capitalist society fundamentally depends on scarcity to function, and so through intellectual property norms we create an artificial scarcity to reward (and incentivize) intellectual labor without questioning our fundamental assumptions about capitalism and value.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fn-7-ref-1&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; But, I digress — let’s revisit the subject in part two.&lt;/p&gt;&lt;p&gt;In part two of this series on intellectual property, I will explain the modern intellectual property regime as I understand it, as well as its history and justification. So equipped with the philosophical and legal background, part three will constitute the bulk of my critique of intellectual property, and my ideals for reform. Part four will examine how these ideas altogether apply in practice to open source, as well as the hairy questions of intellectual property as applied to modern problems in this space, such as the use of LLMs to file the serial numbers off of open source software.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;If you want to dive deeper into the philosophy here, a great resource is the Stanford Encyclopedia of Philosophy. Check out their articles on &lt;a href=&quot;https://plato.stanford.edu/entries/property/&quot; target=&quot;_blank&quot;&gt;Property and Ownership&lt;/a&gt; and &lt;a href=&quot;https://plato.stanford.edu/entries/redistribution/&quot; target=&quot;_blank&quot;&gt;Redistribution&lt;/a&gt; for a start, which expand on some of the ideas I’ve drawn on here and possess a wealth of citations catalogued with a discipline I can never seem to muster for my blog posts. I am a programmer, not a philosopher, so if you want to learn more about this you should go read from the hundreds of years of philosophers who have worked on this with rigor and written down a bunch of interesting ideas.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/On-intellectual-property/</link>
        
        <pubDate>Thu, 13 Feb 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/On-intellectual-property/</guid>
      </item>
    
      <item>
        
        
          <title>Join us to discuss transparency and governance at FOSDEM &apos;25</title>
          <description>
            &lt;p&gt;Good news: it appears that Jack Dorsey’s FOSDEM talk has been cancelled!&lt;/p&gt;&lt;p&gt;&lt;em&gt;This is a follow up to two earlier posts, which you can read here: &lt;a href=&quot;https://drewdevault.com/blog/No-Billionares-at-FOSDEM-please/&quot;&gt;one&lt;/a&gt; and &lt;a href=&quot;https://drewdevault.com/blog/FOSDEM-protest/&quot;&gt;two&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I say it “appears” so, because there has been no official statement from anyone to that effect. There has also been no communication from staff to the protest organizers, including to our email reaching out &lt;a href=&quot;https://fosdem.org/2025/news/2025-01-16-protests/&quot; target=&quot;_blank&quot;&gt;as requested&lt;/a&gt; to discuss fire safety and crowd control concerns with the staff. The situation is a bit unclear, but… we’ll extend FOSDEM the benefit of the doubt, and with it our gratitude. From all of the volunteers who have been organizing this protest action, we extend our heartfelt thanks to the staff for reconsidering the decision to platform Dorsey and Block, Inc. at FOSDEM. All of us – long-time FOSDEM volunteers, speakers, devroom organizers, and attendees – are relieved to know that FOSDEM stands for our community’s interests.&lt;/p&gt;&lt;p&gt;More importantly: what comes next?&lt;/p&gt;&lt;p&gt;The frustration the community felt at learning that Block was sponsoring FOSDEM and one of the keynote slots&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; had been given to Dorsey and his colleagues uncovered some deeper frustrations with the way FOSDEM is run these days. This year is FOSDEM’s 25th anniversary, and it seems sorely overdue for graduating from the “trust us, it’s crazy behind the scenes” governance model to something more aligned with the spirit of open source.&lt;/p&gt;&lt;p&gt;We trust the FOSDEM organizers — we can extend them the benefit of the doubt when they tell us that talk selection is independent of sponsorships. But it strains our presumption of good faith when the talk proposal was &lt;a href=&quot;https://chaos.social/@phoenix/113849512507355397&quot; target=&quot;_blank&quot;&gt;rejected by 3 of the 4&lt;/a&gt; independent reviewers and went through anyway. And it’s kind of weird that we have to take them at their word — that the talk selection process isn’t documented anywhere publicly, nor the conflict of interest policy, nor the sponsorship terms, nor almost anything at all about how FOSDEM operates or is governed internally. Who makes decisions? How? We don’t know, and that’s kind of weird for something so important in the open source space.&lt;/p&gt;&lt;p&gt;Esther Payne, a speaker at FOSDEM 2020, &lt;a href=&quot;https://www.onepict.com/20250122-mirror.html&quot; target=&quot;_blank&quot;&gt;summed up these concerns&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Why do we have so little information on the FOSDEM site about the budget and just how incorporated is FOSDEM as an organisation? How do the laws of Belgium affect the legalities of the organisation? How is the bank account administrated? How much money goes into the costs of this year, and how much of the budget goes into startup costs for the next year?&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Peter Zaitsev, a long-time devroom organizer and FOSDEM speaker for many years, &lt;a href=&quot;https://www.percona.com/blog/in-search-of-transparency-at-fosdem/&quot; target=&quot;_blank&quot;&gt;asked similar questions last year&lt;/a&gt;. I’ve spoken to the volunteers who signed up for the protest – we’re relieved that Dorsey’s talk has been cancelled, but we’re still left with big questions about transparency and governance at FOSDEM.&lt;/p&gt;&lt;p&gt;So, what’s next?&lt;/p&gt;&lt;p&gt;&lt;del&gt;Let’s do something useful with that now-empty time slot in Janson. Anyone who planned to attend the protest is encouraged to come anyway on Sunday at 12:00 PM, where we’re going to talk amongst ourselves and anyone else who shows up about what we want from FOSDEM in the future, and what a transparent and participatory model of governance would look like. We would be thrilled if anyone on the FOSDEM staff wants to join the conversation as well, assuming their busy schedule permits. We’ll prepare a summary of our discussion and our findings to submit to the staff and the FOSDEM community for consideration after the event.&lt;/del&gt;&lt;/p&gt;&lt;p&gt;Until then – I’ll see you there!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;NOTICE&lt;/strong&gt;: The discussion session has been cancelled. After meeting with many of the protest volunteers and discussing the matter among the organizers, we have agreed that de-platforming Dorsey is mission success and improvising further action isn’t worth the trouble. We’ll be moving for reforms at FOSDEM after the event – I’ll keep you posted.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;P.S. It’s a shame we won’t end up handing out our pamphlets. The volunteers working on that came up with this amazing flyer and I think it doesn’t deserve to go unseen:&lt;/p&gt;&lt;style&gt;
.flyers {
  display: flex;
  justify-content: space-around;
}

.flyers a {
  max-width: 45%;
}
&lt;/style&gt;

&lt;div class=&quot;flyers&quot;&gt;
  &lt;a href=&quot;https://redacted.moe/f/abaeed07.pdf&quot;&gt;
    &lt;img
      alt=&quot;Front face of the flyer: &apos;No billionares at FOSDEM&apos; over a picture of Jack Dorsey with the &apos;X&apos; logo over his mouth.&quot;
      src=&quot;https://redacted.moe/f/261d03ea.jpeg&quot;&gt;
  &lt;/a&gt;
  &lt;a href=&quot;https://redacted.moe/f/abaeed07.pdf&quot;&gt;
    &lt;img
      alt=&quot;Reverse face of the flyer, detailing Dorsey and Block, Inc&apos;s numerous misdeeds&quot;
      src=&quot;https://redacted.moe/f/adab02ce.jpeg&quot;&gt;
    &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;We will be doing a modest print run for posterity — find one of us at FOSDEM if you want one.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Transparency-and-governance-FOSDEM/</link>
        
        <pubDate>Thu, 23 Jan 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Transparency-and-governance-FOSDEM/</guid>
      </item>
    
      <item>
        
        
          <title>FOSDEM &apos;25 protest</title>
          <description>
            &lt;p&gt;&lt;em&gt;Update: Dorsey’s talk was cancelled! &lt;a href=&quot;https://drewdevault.com/blog/Transparency-and-governance-FOSDEM/&quot;&gt;See the update here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://drewdevault.com/blog/No-Billionares-at-FOSDEM-please/&quot;&gt;Last week&lt;/a&gt;, I wrote to object to Jack Dorsey and his company, Block, Inc., being accepted as main track speakers at FOSDEM, and proposed a protest action in response. &lt;a href=&quot;https://fosdem.org/2025/news/2025-01-16-protests/&quot; target=&quot;_blank&quot;&gt;FOSDEM issued a statement about our plans&lt;/a&gt; on Thursday.&lt;/p&gt;&lt;p&gt;Today, I have some updates for you regarding the planned action.&lt;/p&gt;&lt;p&gt;I would like to emphasize that we are not protesting FOSDEM or its organizers. We are protesting Jack Dorsey and his company, first and foremost, from promoting their business at FOSDEM. We are members of the FOSDEM community. We have variously been speakers, devroom organizers, volunteers, and attendees for years — in other words, we are not activism tourists. We have a deep appreciation for the organizers and all of the work that they have done over the years to make FOSDEM such a success.&lt;/p&gt;&lt;p&gt;That we are taking action demonstrates that we value FOSDEM, that we believe it represents our community, and that we want to defend its — our — ethos. Insofar as we have a message to the FOSDEM organizers, it is one of gratitude, and an appeal to build a more open and participatory process, in the spirit of open source, and especially to improve the transparency of the talk selection process, sponsorship terms, and conflict of interest policies, so protests like ours are not necessary in the future. To be clear, we do not object to the need for sponsors generally at FOSDEM — we understand that FOSDEM is a free, volunteer driven event, many of us having volunteered for years — but we do object specifically to Jack Dorsey and Block, Inc. being selected as sponsors and especially as speakers.&lt;/p&gt;&lt;p&gt;As for the planned action, I have some more information for anyone who wishes to participate. Our purpose is to peacefully disrupt Dorsey’s talk, and &lt;em&gt;only&lt;/em&gt; Dorsey’s talk, which is scheduled to take place between 12:00 and 12:30 on Sunday, February 2nd in Janson. If you intend to participate, we will be meeting outside of the &lt;em&gt;upper&lt;/em&gt; entrance to Janson at 11:45 AM. We will be occupying the stage for the duration of the scheduled time slot in order to prevent the talk from proceeding as planned.&lt;/p&gt;&lt;p&gt;To maintain the peaceful nature of our protest and minimize the disruption to FOSDEM generally, we ask participants to strictly adhere to the following instructions:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Do not touch anyone else, or anyone else’s property, for any reason.&lt;/li&gt;&lt;li&gt;Do not engage in intimidation.&lt;/li&gt;&lt;li&gt;Remain quiet and peaceful throughout the demonstration.&lt;/li&gt;&lt;li&gt;When the protest ends, disperse peacefully and in a timely manner.&lt;/li&gt;&lt;li&gt;Leave the room the way you found it.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Dorsey’s time slot is scheduled to end at 12:30, but we may end up staying as late as 14:00 to hand the room over to the next scheduled talk.&lt;/p&gt;&lt;p&gt;I’ve been pleased by the response from volunteers (some of whom helped with this update — thanks!), but we still need a few more! I have set up a mailing list for planning the action. If you plan to join, and especially if you’re willing and able to help with additional tasks that need to be organized, please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;contact me directly&lt;/a&gt; to receive an invitation to the mailing list.&lt;/p&gt;&lt;p&gt;Finally, I have some corrections to issue regarding last week’s blog post.&lt;/p&gt;&lt;p&gt;In the days since I wrote my earlier blog post, Dorsey’s talk has been removed from the list of keynotes and moved to the main track, where it will occupy the same time slot in the same room but not necessarily be categorized as a “keynote”.&lt;/p&gt;&lt;p&gt;It has also been pointed out that Dorsey does not bear sole responsibility for Twitter’s sale. However, he is complicit and he profited handsomely from the sale and all of its harmful consequences. The sale left the platform at the disposal of the far right, causing a sharp rise in hate speech and harassment and the layoffs of 3,700 of the Twitter employees that made it worth so much in the first place.&lt;/p&gt;&lt;p&gt;His complicity, along with his present-day activities at Block, Inc. and the priorities of the company that he represents as CEO — its irresponsible climate policy, $120M in fines for enabling consumer fraud, and the layoffs of another 1,000 employees in 2024 despite posting record profits on $5B in revenue — are enough of a threat to our community and its ethos to raise alarm at his participation in FOSDEM. We find this compelling enough to take action to prevent him and his colleagues from using FOSDEM’s platform to present themselves as good actors in our community and sell us their new “AI agentic framework”.&lt;/p&gt;&lt;p&gt;The open source community and FOSDEM itself would not exist without collective action. Our protest to defend its principles is in that spirit. Together we can, and will, de-platform Jack Dorsey.&lt;/p&gt;&lt;p&gt;I’ll see you there!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/FOSDEM-protest/</link>
        
        <pubDate>Mon, 20 Jan 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/FOSDEM-protest/</guid>
      </item>
    
      <item>
        
        
          <title>No billionaires at FOSDEM</title>
          <description>
            &lt;p&gt;&lt;em&gt;Update: Dorsey’s talk was cancelled! &lt;a href=&quot;https://drewdevault.com/blog/Transparency-and-governance-FOSDEM/&quot;&gt;See the update here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Jack Dorsey, former CEO of Twitter, ousted board member of BlueSky, and grifter extraordinaire to the tune of a $5.6B net worth, is &lt;a href=&quot;https://fosdem.org/2025/schedule/event/fosdem-2025-4507-infusing-open-source-culture-into-company-dna-a-conversation-with-jack-dorsey-and-manik-surtani-block-s-head-of-open-source/&quot; target=&quot;_blank&quot;&gt;giving a keynote at FOSDEM&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The FOSDEM keynote stage is one of the biggest platforms in the free software community. Janson is the biggest venue in the event – its huge auditorium can accommodate over 1,500 of FOSDEM’s 8,000 odd attendees, and it is live streamed to a worldwide audience as the face of one of the free and open source software community’s biggest events of the year. We’ve platformed Red Hat, the NLNet Foundation, NASA, numerous illustrious community leaders, and many smaller projects that embody our values and spirit at this location to talk about their work or important challenges our community faces.&lt;/p&gt;&lt;p&gt;Some of these challenges, as a matter of fact, are Jack Dorsey’s fault. In 2023 this stage &lt;a href=&quot;https://archive.fosdem.org/2023/schedule/event/hachyderm/&quot; target=&quot;_blank&quot;&gt;hosted Hachyderm’s Kris Nóva&lt;/a&gt; to discuss an exodus of Twitter refugees to the fediverse. After Dorsey sold Twitter to Elon Musk, selling the platform out to the far right for a crisp billion-with-a-“B” dollar payout, the FOSS community shouldered the burden – both with our labor and our wallets – of a massive exodus onto our volunteer-operated servers, especially from victims fleeing the hate speech and harassment left in the wake of the sale. Two years later one of the principal architects of, and beneficiaries of, that disaster will step onto the same stage. Even if our community hadn’t been directly harmed by Dorsey’s actions, I don’t think that we owe this honor to someone who took a billion dollars to ruin their project, ostracize their users, and destroy the livelihoods of almost everyone who worked on it.&lt;/p&gt;&lt;p&gt;Dorsey is presumably being platformed in Janson because his blockchain bullshit company is a &lt;a href=&quot;https://fosdem.org/2025/about/sponsors/&quot; target=&quot;_blank&quot;&gt;main sponsor of FOSDEM&lt;/a&gt; this year. Dorsey and his colleagues want to get us up to speed on what Block is working on these days. Allow me to give you a preview: in addition to posting $5B in revenue and a 21% increase in YoY profit in 2024, Jack Dorsey laid off 1,000 employees, ordering them not to publicly discuss board member Jay-Z’s contemporary sexual assault allegations on their way out, and announced a new bitcoin mining ASIC in collaboration with Core Scientific, who presumably installed them into &lt;a href=&quot;https://investors.corescientific.com/news-events/press-releases/detail/99/core-scientific-and-port-muskogee-break-ground-on-100-mw-hpc-data-center&quot; target=&quot;_blank&quot;&gt;their new 100MW Muskogee, OK bitcoin mining installation&lt;/a&gt;, proudly served by the &lt;a href=&quot;https://www.gem.wiki/Muskogee_Generating_Station&quot; target=&quot;_blank&quot;&gt;Muskogee Generating Station&lt;/a&gt; fossil fuel power plant and its &lt;a href=&quot;https://www.gem.wiki/Muskogee_Generating_Station#Emissions_Data&quot; target=&quot;_blank&quot;&gt;11 million tons&lt;/a&gt; of annual CO&lt;sub&gt;2&lt;/sub&gt; emissions and &lt;a href=&quot;https://www.gem.wiki/Muskogee_Generating_Station#Death_and_disease_attributable_to_fine_particle_pollution_from_Muskogee_Generating_Station&quot; target=&quot;_blank&quot;&gt;an estimated 62 excess deaths&lt;/a&gt; in the local area due to pollution associated with the power plant. Nice.&lt;/p&gt;&lt;p&gt;In my view, billionaires are not welcome at FOSDEM. If billionaires want to participate in FOSS, I’m going to ask them to refrain from using our platforms to talk about their AI/blockchain/bitcoin/climate-disaster-as-a-service grifty business ventures, and instead buy our respect by, say, donating 250 million dollars to &lt;a href=&quot;https://nlnet.nl/&quot; target=&quot;_blank&quot;&gt;NLNet&lt;/a&gt; or the &lt;a href=&quot;https://www.sovereign.tech/&quot; target=&quot;_blank&quot;&gt;Sovereign Tech Fund&lt;/a&gt;. That figure, as a percentage of Dorsey’s wealth, is proportional to the amount of money I donate to FOSS every year, by the way. That kind of money would keep the FOSS community running for decades.&lt;/p&gt;&lt;p&gt;I do not want to platform Jack Dorsey on this stage. To that end, I am organizing a sit-in, in which I and anyone who will join me are going to sit ourselves down on the Janson stage during his allocated time slot and peacefully prevent the talk from proceeding as scheduled. We will be meeting at 11:45 AM outside of Janson, 15 minutes prior to Dorsey’s scheduled time slot. Once the stage is free from the previous speaker, we will sit on the stage until 12:30 PM. Bring a good book. If you want to help organize this sit-in, or just let me know that you intend to participate, please contact me via &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email&lt;/a&gt;; I’ll set up a mailing list if there’s enough interest in organizing things like printing out pamphlets to this effect, or even preparing an alternative talk to “schedule” in his slot.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;a href=&quot;https://drewdevault.com/2025/01/20/2025-01-20-FOSDEM-protest.html&quot; target=&quot;_blank&quot;&gt;Follow-up: FOSDEM ’25 protest&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/No-Billionares-at-FOSDEM-please/</link>
        
        <pubDate>Thu, 16 Jan 2025 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/No-Billionares-at-FOSDEM-please/</guid>
      </item>
    
      <item>
        
        
          <title>Neurodivergence and accountability in free software</title>
          <description>
            &lt;p&gt;In November of last year, I wrote &lt;a href=&quot;https://drewdevault.com/2023/11/25/2023-11-26-RMS-on-sex.html&quot; target=&quot;_blank&quot;&gt;Richard Stallman’s political discourse on sex&lt;/a&gt;, which argues that Richard Stallman, the founder of and present-day voting member of the board of directors of the Free Software Foundation (FSF), endorses and advocates for a harmful political agenda which legitimizes adult attraction to minors, consistently defends adults accused of and convicted of sexual crimes with respect to minors, and more generally erodes norms of consent and manipulates language regarding sexual harassment and sexual assault in his broader political program.&lt;/p&gt;&lt;p&gt;In response to this article, and on many occasions when I have re-iterated my position on Stallman in other contexts, a common response is to assert that my calls to censure Stallman are ableist, on the basis that Stallman is neurodivergent (ND). This line of reasoning suggests that Stallman’s awkward and zealous views on sex are in line with his awkward and zealous positions on other matters (such as his insistence on “GNU/Linux” terminology rather than “Linux”), and that together this illustrates a pattern which suggests neurodivergence is at play. This argumentation is flawed, but I think it presents us with a good opportunity to talk about how neurodivergence and sexism presents in our community.&lt;/p&gt;&lt;p&gt;Neurodivergence (antonymous with “neurotypical”) is an umbrella term that encompasses a wide variety of human experiences, including autism, ADHD, personality disorders, bipolar disorder, and others. The particular claims I’ve heard about Stallman suggest that he is “obviously” autistic, or has &lt;a href=&quot;https://en.wikipedia.org/wiki/Asperger_syndrome&quot; target=&quot;_blank&quot;&gt;Asperger syndrome&lt;/a&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; The allegation of ableism in my criticisms of Stallman are rooted in this presumption of neurodivergence in Stallman: the argument goes that I am putting his awkwardness on display and mocking him for it, that calling for the expulsion of someone on the basis of being awkward is ableist, and that this has a chilling effect on our community, which is generally thought to have a high incidence of neurodivergence. I will respond to this defense of Stallman today.&lt;/p&gt;&lt;p&gt;A defense of problematic behavior that cites neurodivergence to not only explain, but excuse, said behavior, is ableist and harms neurodivergent people, rather than standing up for them as these arguments portray themselves as doing. To illustrate this, I opened a discussion on the Fediverse asking neurodivergent people to chime in and reached out directly to some ND friends in my social circle.&lt;/p&gt;&lt;hr&gt;&lt;h3&gt;Aside: Is Stallman neurodivergent?&lt;/h3&gt;&lt;p&gt;Stallman’s neurodivergence is an unsolicited armchair diagnosis with no supporting evidence besides “vibes”. &lt;a href=&quot;https://www.computerworld.com/article/1683286/asperger-s-oxymoron.html&quot; target=&quot;_blank&quot;&gt;This 2008 article&lt;/a&gt; summarizes his public statements on the subject:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;“During a 2000 profile for the Toronto Star, Stallman described himself to an interviewer as ‘borderline autistic,’ a description that goes a long way toward explaining a lifelong tendency toward social and emotional isolation and the equally lifelong effort to overcome it,” Williams wrote.&lt;/p&gt;&lt;p&gt;When I cited that excerpt from the book during the interview, Stallman said that assessment was “exaggerated.”&lt;/p&gt;&lt;p&gt;“I wonder about it, but that’s as far as it goes,” he said. “Now, it’s clear I do not have [Asperger’s] — I don’t have most of the characteristics of that. For instance, one of those characteristics is having trouble with rhythm. I love the most complicated, fascinating rhythms.” But Stallman did acknowledge that he has “a few of the characteristics” and that he “might have what some people call a ‘shadow’ version of it.”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The theory that Stallman is neurodivergent is usually cited to explain his various off-putting behaviors, but there is no tangible evidence to support the theory. This alone raises some alarms, in that off-putting behavior is sufficient evidence to presume neurodivergence. I agree that some of his behavior, off-putting or otherwise, appears consistent, to my untrained eye, with some of the symptoms of autism. Nevertheless I am not going to forward an armchair diagnosis in either direction. However, because a defense of Stallman on the basis of neurodivergence is contingent on him being neurodivergent, this rest of this article will presume that it is true for the purpose of rebuttal.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;: we don’t know and the assumption that he is is ableist.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;This defense of Stallman is ableist because it infantalizes and denies agency to neurodivergent people. Consider what’s being said here: it only follows that Stallman’s repugnant behavior is excusable because he’s neurodivergent if neurodivergent people cannot help but be repugnant. An autistic person I spoke to, who wishes to remain anonymous, had the following to say:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;As an autistic person, I find these statements deeply offensive, because they build on and perpetuate damaging stereotypes.&lt;/p&gt;&lt;p&gt;Research has repeatedly proved that, on average, autistic folks have high empathy and a higher sense of values than the general population. We are not the emotionless robots that the popular imagination believes we are.&lt;/p&gt;&lt;p&gt;But we are not a monolith, and some autistic folks are absolute assholes who should be called out (and held accountable) for the harm that they cause. Autism is context, not an excuse: it can explain why someone might struggle in some situations and need additional support, but it should never be an excuse to harm others. We can all learn and improve.&lt;/p&gt;&lt;p&gt;I have witnessed people pulling the autism card to avoid consequences for CoC violations, then calling out the organization for “not supporting true diversity” when they’re shown the door. This is manipulative and insulting to the other neurodivergent members of the community, and should never be tolerated.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Bram Dingelstad, a neurodivergent person who participated in the discussion, had this to say:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Problematic behaviour is what it is: problematic.&lt;/p&gt;&lt;p&gt;There are a lot of neurodivergent people out there that are able to carry themselves in a way that doesn’t make anyone unsafe or harm victims of sexual assault by dismissing or downplaying their lived experience. In my opinion, using neurodivergence as an excuse for this behaviour only worsens the perception of neurodiversity.&lt;/p&gt;&lt;p&gt;Richard Stallman should be held accountable for his speech and his actions.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Another commenter put it more concisely, if not as eloquently:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It’s fucking ableist to say neurodiversity disposes you towards problematic behaviors. It’s disgusting trying to hide behind it and really quite insulting.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I came away from these discussions with the following understanding: neurodivergence, in particular autism, causes people to struggle to understand unstated social norms and conventions, sometimes with embarrassing or harmful consequences, such as with respect to interpersonal relationships. The people I’ve spoken to call for empathy and understanding in the mistakes which can be made in light of this, but also call for accountability – to be shown what’s right (and, importantly, &lt;em&gt;why&lt;/em&gt; it’s so), and then to be expected to behave accordingly, no different from anyone else.&lt;/p&gt;&lt;p&gt;Being neurodivergent doesn’t make someone sexist, but it can make it harder for them to hide sexist views. To associate Stallman’s sexism with his perceived neurodivergence is ableist, and to hold Stallman accountable for his behavior is not. One commenter puts it this way:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I’ve said quite a few times is that sexism is not a symptom of autism. Writing this sort of behaviour off as “caused by” neurodivergence is itself ableist, I’m not a huge fan of the narrative that I have “the neurodevelopmental disorder that makes you a bigot”.&lt;/p&gt;&lt;p&gt;I fundamentally disagree with the idea that the pervasive sexism in tech is because of the high incidence of neurodiversity. It’s because tech has broadly operated as a boys club for decades, and those norms persist.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Using neurodivergence as a cover for sexism and problematic behavior in our communities is a toxic, ableist, and, of course, sexist attitude that serves to provide problematic men with space to be problematic. Note also how intersections between neurodiversity and identity play out: white men tend to be excused on the basis of neurodivergence, whereas for women, transgender people, people of color, etc – the excuse does not apply. Consider the differences in how bipolar disorder is perceived in women – “she’s crazy” – versus how men with autism are accommodated – “he can’t help it”.&lt;/p&gt;&lt;p&gt;So, I reject the notion that it is ableist to criticize problematic behavior that can be explained by neurodivergence. But, even if it were, an anonymous autistic commenter has this to say:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If we accept the hypothesis that it is ableist to condemn behavior which can be explained by neurodivergence (and I don’t), my answer is: be ableist. I don’t like it, but it’s ridiculous to imagine any other option in the physical world, and it’s weird to treat the virtual world so differently.&lt;/p&gt;&lt;p&gt;Here’s an anecdote: when I was at school, a new person, Adam, joined the class. We didn’t want Adam to feel excluded, so we included him in our social events. Adam had narcissistic personality disorder, and likely in part because of this, he was also a serial harasser of women. So what did we do about it?&lt;/p&gt;&lt;p&gt;We stopped inviting Adam. I wish we didn’t have to stop inviting him, but our hands were tied. I’m not going to say it’s something only he could change, because maybe he truly couldn’t change that. Maybe it was ableist to exclude him. But the safety of my friends comes first. The hard part is distinguishing between this situation and a situation where someone is excluded when they are perceived as a threat just because they’re different.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Stallman’s rhetoric and behavior are harmful, and we need to address that harm. The refrain of “criticizing Stallman’s behavior is ableist and alienates neurodiverse individuals in our community” is itself ableist and isn’t doing any favors for our neurodiverse friends.&lt;/p&gt;&lt;p&gt;To conclude this article, I thought I’d take this opportunity to find out what our neurodiverse friends are actually struggling with and how we can better accommodate their needs in our community.&lt;/p&gt;&lt;p&gt;First of all, a recognition of individuals as being autonomous, independent people with agency and independent needs has to come first, with neurodiversity and with everything else. Listen to people when they explain their experiences and their needs as individuals, and don’t rely on romanticized and stereotypical understandings of particular neurodevelopmental conditions such as autism. These stereotypes are often deeply harmful: one person spoke of being accused of incompetence and lying about their neurodivergence in a ploy for sympathy. They experienced severe harassment, at the worst in the form of harassers engineering stressful situations and screenshoting their reactions to humiliate them and damage their reputation.&lt;/p&gt;&lt;p&gt;Standing up for your peers is important, in this as in all things. Not only against harassment, discrimination, and abuse on the basis of neurodivergence, but on any basis, from any person – which I was often reminded is especially important for neurodivergent people who are not cishet white men, as these challenges are amplified in light of these intersectional identities. Talk to people and understand their experiences, their needs, and their worldview. Be patient, but clear and open in your communication. The neurodivergent people I spoke to often found it difficult to learn social mores, moreso than most neurotypical experiences, but nevertheless the vast majority of them felt perfectly capable of it, and the expectation that they weren’t is demeaning and ableist.&lt;/p&gt;&lt;p&gt;I also heard some advice from the neurodivergent community that applies especially to free software community leaders. Clearly stated community norms and expectations, through codes of conducts and visible moderation, is often helpful for neurodivergent people. Many ND people struggle to intuit or “guess” social norms and prefer expectations to be stated unambiguously. Normalizing the use of tone indicators (e.g. “/s”), questions clarifying intent, and conflict de-escalation are also good tools to employ.&lt;/p&gt;&lt;p&gt;Another consideration of merit is accommodations for asynchronous participation in meaningful governance and decision-making processes. Some ND people find it difficult to participate in real-time discussions in chat rooms or in person, and mediums like emails and other long-form slow discussions are easier for them to engage with. Accommodations for sensory sensitivities at in-person events is another good strategy to include more ND folks in your event. Establishing quiet spaces to get away from the busier parts of the event, being considerate of lighting choices, flexible break times, and activities for smaller groups were all highlighted to me by ND people as making their experience more enjoyable.&lt;/p&gt;&lt;p&gt;These are the lessons I took away from speaking to dozens of neurodivergent people in researching this blog post. I encourage you to speak to, and listen to, people in your communities as well, particularly when dealing with an issue which cites their struggles or impacts them directly.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Neurodivergence-and-accountability-in-free-software/</link>
        
        <pubDate>Wed, 25 Sep 2024 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Neurodivergence-and-accountability-in-free-software/</guid>
      </item>
    
      <item>
        
        
          <title>Rust for Linux revisited</title>
          <description>
            &lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Ugh. Drew’s blogging about Rust again.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;– You&lt;/p&gt;&lt;p&gt;I promise to be nice.&lt;/p&gt;&lt;p&gt;Two years ago, seeing the Rust-for-Linux project starting to get the ball rolling, I wrote “&lt;a href=&quot;https://drewdevault.com/2022/10/03/Does-Rust-belong-in-Linux.html&quot; target=&quot;_blank&quot;&gt;Does Rust belong in the Linux kernel?&lt;/a&gt;”, penning a conclusion consistent with &lt;a href=&quot;https://en.wikipedia.org/wiki/Betteridge&apos;s_law_of_headlines&quot; target=&quot;_blank&quot;&gt;Betteridge’s law of headlines&lt;/a&gt;. Two years on we have a lot of experience to draw on to see how Rust-for-Linux is actually playing out, and I’d like to renew my thoughts with some hindsight – and more compassion. If you’re one of the Rust-for-Linux participants burned out or burning out on this project, I want to help. Burnout sucks – I’ve been there.&lt;/p&gt;&lt;p&gt;The people working on Rust-for-Linux are incredibly smart, talented, and passionate developers who have their eyes set on a goal and are tirelessly working towards it – and, as time has shown, with a great deal of patience. Though I’ve developed a mostly-well-earned reputation for being a fierce critic of Rust, I do believe it has its place and I have a lot of respect for the work these folks are doing. These developers are ambitious and motivated to make an impact, and Linux is undoubtedly the highest-impact software in the world, and in theory Linux is enthusiastically ready to accept motivated innovators into its fold to facilitate that impact.&lt;/p&gt;&lt;p&gt;At least in theory. In practice, the Linux community is the wild wild west, and sweeping changes are infamously difficult to achieve consensus on, and this is by far the broadest sweeping change ever proposed for the project. Every subsystem is a private fiefdom, subject to the whims of each one of Linux’s 1,700+ maintainers, almost all of whom have a dog in this race. It’s herding cats: introducing Rust effectively is one part coding work and ninety-nine parts political work – and it’s a lot of coding work. Every subsystem has its own unique culture and its own strongly held beliefs and values.&lt;/p&gt;&lt;p&gt;The consequences of these factors is that Rust-for-Linux has become a burnout machine. My heart goes out to the developers who have been burned in this project. It’s not fair. Free software is about putting in the work, it’s a classical do-ocracy… until it isn’t, and people get hurt. In spite of my critiques of the project, I recognize the talent and humanity of everyone involved, and wouldn’t have wished these outcomes on them. I also have sympathy for many of the established Linux developers who didn’t exactly want this on their plate… but that’s neither here nor there for the purpose of this post, and any of those developers and their fiefdoms who went out of their way to make life &lt;em&gt;difficult&lt;/em&gt; for the Rust developers above and beyond what was needed to ensure technical excellence are accountable for these shitty outcomes.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;So where do we go now?&lt;/p&gt;&lt;p&gt;Well, let me begin by re-iterating something from my last article on the subject: “I wish [Rust-for-Linux] the best of luck and hope to see them succeed”. Their path is theirs to choose, and though I might advise a moment to rest before diving headfirst into this political maelstrom once again, I support you in your endeavours if this is what you choose to do. Not my business. That said, allow me to humbly propose a different path for your consideration.&lt;/p&gt;&lt;p&gt;Here’s the pitch: a motivated group of talented Rust OS developers could build a Linux-compatible kernel, from scratch, very quickly, with no need to engage in LKML politics. You would be astonished by how quickly you can make meaningful gains in this kind of environment; I think if the amount of effort being put into Rust-for-Linux were applied to a new Linux-compatible OS we could have something production ready for some use-cases within a few years.&lt;/p&gt;&lt;p&gt;Novel OS design is hard: projects like &lt;a href=&quot;https://www.redox-os.org/&quot; target=&quot;_blank&quot;&gt;Redox&lt;/a&gt; are working on this, but it will take a long time to bear fruit and research operating systems often have to go back to the drawing board and make major revisions over and over again before something useful and robust emerges. This is important work – and near to my heart – but it’s not for everyone. However, making an OS which is based on a proven design like Linux is &lt;em&gt;much&lt;/em&gt; easier and can be done very quickly. I worked on my own novel OS design for a couple of years and it’s still stuck in design hell and badly in need of being rethought; on the other hand I wrote a passable Unix clone alone in less than 30 days.&lt;/p&gt;&lt;p&gt;Rust is a great fit for a large monolithic kernel design like Linux. Imagine having the opportunity to implement something like the dcache from scratch in Rust, without engaging with the politics – that’s something a small group of people, perhaps as few as one, could make substantial inroads on in a short period of time taking full advantage of what Rust has on offer. Working towards compatibility with an existing design can leverage a much larger talent pool than the very difficult problem of novel OS design, a lot of people can manage with a copy of the ISA manual and a missive to implement a single syscall in a Linux-compatible fashion over the weekend. A small and motivated group of contributors could take on the work of, say, building out io_uring compatibility and start finding wins fast – it’s a lot easier than designing io_uring from scratch. I might even jump in and build out a driver or two for fun myself, that sounds like a good opportunity for me to learn Rust properly with a fun project with a well-defined scope.&lt;/p&gt;&lt;p&gt;Attracting labor shouldn’t be too difficult with this project in mind, either. If there was &lt;em&gt;the&lt;/em&gt; Rust OS project, with a well-defined scope and design (i.e. aiming for Linux ABI compatibility), I’m sure there’s a lot of people who’d jump in to stake a claim on some piece of the puzzle and put it together, and the folks working on Rust-for-Linux have the benefit of a great deal of experience with the Linux kernel to apply to oversight on the broader design approach. Having a clear, well-proven goal in mind can also help to attract the same people who want to make an impact in a way that a speculative research project might not. Freeing yourselves of the LKML political battles would probably be a big win for the ambitions of bringing Rust into kernel space. Such an effort would also be a great way to mentor a new generation of kernel hackers who are comfortable with Rust in kernel space and ready to deploy their skillset to the research projects that will build a next-generation OS like Redox. The labor pool of serious OS developers badly needs a project like this to make that happen.&lt;/p&gt;&lt;p&gt;So my suggestion for the Rust-for-Linux project is: you’re burned out and that’s awful, I feel for you. It might be fun and rewarding to spend your recovery busting out a small prototype Unix kernel and start fleshing out bits and pieces of the Linux ABI with your friends. I can tell you from my own experience doing something very much like this that it was a very rewarding burnout recovery project for me. And who knows where it could go?&lt;/p&gt;&lt;p&gt;Once again wishing you the best and hoping for your success, wherever the path ahead leads.&lt;/p&gt;&lt;details class=&quot;block&quot;&gt;&lt;summary&gt;What about drivers?&lt;/summary&gt;&lt;p&gt;To pre-empt a response I expect to this article: there’s the annoying question of driver support, of course. This was an annoying line of argumentation back when Linux had poor driver support as well, and it will be a nuisance for a hypothetical Linux-compatible Rust kernel as well. Well, the same frustrated arguments I trotted out then are still ready at hand: you choose your use-cases carefully. General-purpose comes later. Building an OS which supports virtual machines, or a datacenter deployment, or a specific mobile device whose vendor is volunteering labor for drivers, and so on, will come first. You choose the hardware that supports the software, not the other way around, or build the drivers you need.&lt;/p&gt;&lt;p&gt;That said, a decent spread of drivers should be pretty easy to implement with the talent base you have at your disposal, so I wouldn’t worry about it.&lt;/p&gt;&lt;/details&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Rust-in-Linux-revisited/</link>
        
        <pubDate>Fri, 30 Aug 2024 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Rust-in-Linux-revisited/</guid>
      </item>
    
      <item>
        
        
          <title>So you want to compete with or replace open source</title>
          <description>
            &lt;p&gt;We are living through an interesting moment in source-available software.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; The open source movement has always had, and continues to have, a solid grounding in grassroots programmers building tools for themselves and forming communities around them. Some looming giants brought on large sums of money – Linux, Mozilla, Apache, and so on – and other giants made do without, like GNU, but for the most part if anyone thought about open source 15 years ago they were mostly thinking about grassroots communities who built software together for fun. With the rise of GitHub and in particular the explosion of web development as an open platform, commercial stakeholders in software caught on to the compelling economics of open source. The open source boom that followed caused open source software to have an enormous impact on everyone working in the software industry, and, in one way or another, on everyone living on planet Earth.&lt;/p&gt;&lt;p&gt;Over the past decade or so, a lot of businesses, particularly startups, saw these economics unfolding in front of them and wanted to get in on this boom. A lot of talented developers started working on open source software with an explicit aim towards capitalizing on it, founding businesses and securing capital investments to build their product – an open source product. A few years following the onset of these startups, the catch started to become apparent. While open source was proven to be incredibly profitable and profoundly useful for the software industry &lt;em&gt;as a whole&lt;/em&gt;, the economics of making open source work for &lt;em&gt;one business&lt;/em&gt; are much different.&lt;/p&gt;&lt;p&gt;It comes down to the fact that the free and open source software movements are built on collaboration, and all of our success is attributable to this foundation. The economics that drew commercial interest into the movement work specifically because of this collaboration – because the FOSS model allows businesses to share R&amp;D costs and bring together talent across corporate borders into a great melting pot of innovation. And, yes, there is no small amount of exploitation going on as well; businesses are pleased to take advantage of the work of Jane Doe in Ohio’s FOSS project to make themselves money without sharing any of it back. Nevertheless, the revolutionary economics of FOSS are &lt;em&gt;based on&lt;/em&gt; collaboration, and are &lt;em&gt;incompatible with&lt;/em&gt; competition.&lt;/p&gt;&lt;p&gt;The simple truth of open source is that if you design your business model with an eye towards competition, in which you are the only entity who can exclusively monetize the software product, you must eschew the collaborative aspects of open source – and thus its greatest strength. Collaboration in open source works because the collaborators, all representatives of different institutions, are incentivized to work together for mutual profit. No one is incentivized to work for you, for free, for your own exclusive profit.&lt;/p&gt;&lt;p&gt;More than a few of these open source startups were understandably put out when this reality started to set in. It turns out the market capitalization of a business that has an open source product was often smaller than the investments they had brought in. Under these conditions it’s difficult to give the investors the one and only thing they demand – a return on investment. The unbounded growth demanded by the tech boom is even less likely to be attainable in open source. There are, to be entirely clear, many business models which are compatible with open source. But there are also many which are not. There are many open source projects which can support a thriving business or even a thriving sub-industry, but there are some ideas which, when placed in an open source framing, simply cannot be capitalized on as effectively, or often at all.&lt;/p&gt;&lt;p&gt;Open source ate a lot of lunches. There are some kinds of software which you just can’t make in a classic silicon valley startup fashion anymore. Say you want to write a database server – a sector which has suffered a number of rug-pulls from startups previously committed to open source. If you make it closed source, you can’t easily sell it like you could 10 or 20 years ago, ala MSSQL. This probably won’t work. If you make it open source, no one will pay you for it and you’ll end up moaning about how the major cloud providers are “stealing” your work. The best way to fund the development of something like that is with a coalition of commercial stakeholders co-sponsoring or co-maintaining the project in their respective self-interests, which is how projects like &lt;a href=&quot;https://www.postgresql.org/about/contributing/&quot; target=&quot;_blank&quot;&gt;PostgreSQL&lt;/a&gt;, &lt;a href=&quot;https://mesa.freedesktop.org/developers/&quot; target=&quot;_blank&quot;&gt;Mesa&lt;/a&gt;, or the Linux kernel attract substantial paid development resources. But it doesn’t really work as a startup anymore.&lt;/p&gt;&lt;p&gt;Faced with these facts, there have been some challenges to the free and open source model coming up in the past few years, some of which are getting organized and starting to make serious moves. Bruce Perens, one of the founding figures of the Open Source Initiative, is working on the “post-open” project; “Fair Source” is another up-and-coming-effort, and there have been and will be others besides.&lt;/p&gt;&lt;p&gt;What these efforts generally have in common is a desire to change the commercial dynamic of source-available software. In other words, the movers and shakers in these movements want to get paid more, or more charitably, want to start a movement in which programmers that work on source-available software as a broader class get paid more. The other trait they have in common is a view that the open source definition and the four freedoms of free software do not sufficiently provide for this goal.&lt;/p&gt;&lt;p&gt;For my part, I don’t think that this will work. I think that the aim of sole or limited rights to monetization and the desire to foster a collaborative environment are irreconcilable. These movements want to have both, and I simply don’t think that’s possible.&lt;/p&gt;&lt;p&gt;This logic is rooted in a deeper notion of ownership over the software, which is both subtle and very important. This is a kind of &lt;a href=&quot;https://en.wikipedia.org/wiki/Auteur&quot; target=&quot;_blank&quot;&gt;auteur&lt;/a&gt; theory of software. The notion is that the software they build &lt;em&gt;belongs&lt;/em&gt; to them. They possess a sense of ownership over the software, which comes with a set of moral and perhaps legal rights to the software, which, importantly, are withheld from any entity other than themselves. The “developers” enjoy this special relationship with the project – the “developers” being the special class of person entitled to this sense of ownership and the class to whom the up-and-coming source-available movements make an appeal, in the sense of “pay the developers” – and third-party entities who work on the source code are merely “contributors”, though they apply the same skills and labor to the project as the “developers” do. The very distinction between “first-party” and “third-party” developers is contingent on this “auteur” worldview.&lt;/p&gt;&lt;p&gt;This is quite different from how most open source projects have found their wins. If Linux can be said to belong to anyone, it belongs to everyone. It is for this reason that it is in everyone’s interests to collaborate on the project. If it belonged to someone or some entity alone, especially if that sense of ownership is rooted in justifying that entity’s sole right to effectively capitalize on the software, the dynamic breaks down and the incentive for the “third-party” class to participate is gone. It doesn’t work.&lt;/p&gt;&lt;p&gt;That said, clearly the proponents of these new source-available movements feel otherwise. And, to be clear, I wish them well. I respect the right for authors of software to distribute it under whatever terms they wish.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; And, for my part, I do believe that source-available is a clear improvement over proprietary software, even though these models fall short of what I perceive as the advantages of open source. However, for these movements to have a shot at success, they need to deeply understand these dynamics and the philosophical and practical underpinnings of the free and open source movements.&lt;/p&gt;&lt;p&gt;However, it is very important to me that we do not muddy the landscape of open source by trying to reform, redefine, or expand our understanding of open source to include movements which contradict this philosophy. My well-wishes are contingent on any movements which aim to compete with open source stopping short of &lt;em&gt;calling themselves&lt;/em&gt; open source. This is something I appreciate about the fair source and post-open movements – both movements explicitly disavow the label of open source. If you want to build something new, be clear that it is something new – this is the ground rule.&lt;/p&gt;&lt;p&gt;So you want to compete with open source, or even replace it with something new. Again, I wish you good luck. But this question will be at the heart of your challenge: will you be able to assume the mantle of the auteur and capitalize on this software while still retaining the advantages that made open source successful? Will you be able to appeal to the public in the same way open source does while holding onto these commercial advantages for yourself? Finding a way to answer this question with a “yes” is the task laid before you. It will be difficult; in the end, you will have to give something to the public to get something in return. Simply saying that the software itself is a gift equal to the labor you ask of the public is probably not going to work, especially when this “gift” comes with monetary strings attached.&lt;/p&gt;&lt;p&gt;As for me, I still believe in open source, and even in the commercial potential of open source. It requires creativity and a clever business acumen to identify and exploit market opportunities within this collaborative framework. To win in open source you must embrace this collaboration and embrace the fact that you will share the commercial market for the software with other entities. If you’re up to that challenge, then let’s keep beating the open source drum together. If not, these new movements may be a home for you – but know that a lot of hard work still lies ahead of you in that path.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/So-you-want-to-compete-with-FOSS/</link>
        
        <pubDate>Tue, 16 Jul 2024 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/So-you-want-to-compete-with-FOSS/</guid>
      </item>
    
      <item>
        
        
          <title>Writing a Unix clone in about a month</title>
          <description>
            &lt;p&gt;I needed a bit of a break from “real work” recently, so I started a new programming project that was low-stakes and purely recreational. On April 21st, I set out to see how much of a Unix-like operating system for x86_64 targets that I could put together in about a month. The result is &lt;a href=&quot;https://git.sr.ht/~sircmpwn/bunnix&quot; target=&quot;_blank&quot;&gt;Bunnix&lt;/a&gt;. Not including days I didn’t work on Bunnix for one reason or another, I spent 27 days on this project.&lt;/p&gt;&lt;p&gt;You can try it for yourself if you like:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://cyanide.ayaya.dev/bunnix.iso&quot; target=&quot;_blank&quot;&gt;Bunnix 0.0.0 iso&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To boot this ISO with qemu:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;qemu-system-x86_64 -cdrom bunnix.iso -display sdl -serial stdio
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also write the iso to a USB stick and boot it on real hardware. It will probably work on most AMD64 machines – I have tested it on a ThinkPad X220 and a Starlabs Starbook Mk IV. Legacy boot and EFI are both supported. There are some limitations to keep in mind, in particular that there is no USB support, so a PS/2 keyboard (or PS/2 emulation via the BIOS) is required. Most laptops rig up the keyboard via PS/2, and &lt;abbr title=&quot;your milage may vary&quot;&gt;YMMV&lt;/abbr&gt; with USB keyboards via PS/2 emulation.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Tip: the DOOM keybindings are weird. WASD to move, right shift to shoot, and space to open doors. Exiting the game doesn’t work so just reboot when you’re done playing. I confess I didn’t spend much time on that port.&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;What’s there?&lt;/h2&gt;&lt;p&gt;The Bunnix kernel is (mostly) written in &lt;a href=&quot;https://harelang.org&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt;, plus some C components, namely lwext4 for ext4 filesystem support and libvterm for the kernel video terminal.&lt;/p&gt;&lt;p&gt;The kernel supports the following drivers:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;PCI (legacy)&lt;/li&gt;&lt;li&gt;AHCI block devices&lt;/li&gt;&lt;li&gt;GPT and MBR partition tables&lt;/li&gt;&lt;li&gt;PS/2 keyboards&lt;/li&gt;&lt;li&gt;Platform serial ports&lt;/li&gt;&lt;li&gt;CMOS clocks&lt;/li&gt;&lt;li&gt;Framebuffers (configured by the bootloaders)&lt;/li&gt;&lt;li&gt;ext4 and memfs filesystems&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There are numerous supported kernel features as well:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A virtual filesystem&lt;/li&gt;&lt;li&gt;A /dev populated with block devices, null, zero, and full psuedo-devices, /dev/kbd and /dev/fb0, serial and video TTYs, and the /dev/tty controlling terminal.&lt;/li&gt;&lt;li&gt;Reasonably complete terminal emulator and somewhat passable termios support&lt;/li&gt;&lt;li&gt;Some 40 syscalls, including for example clock_gettime, poll, openat et al, fork, exec, pipe, dup, dup2, ioctl, etc&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Bunnix is a single-user system and does not currently attempt to enforce Unix file modes and ownership, though it could be made multi-user relatively easily with a few more days of work.&lt;/p&gt;&lt;p&gt;Included are two bootloaders, one for legacy boot which is multiboot-compatible and written in Hare, and another for EFI which is written in C. Both of them load the kernel as an ELF file plus an initramfs, if required. The EFI bootloader includes zlib to decompress the initramfs; multiboot-compatible bootloaders handle this decompression for us.&lt;/p&gt;&lt;p&gt;The userspace is largely assembled from third-party sources. The following third-party software is included:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Colossal Cave Adventure (advent)&lt;/li&gt;&lt;li&gt;dash (/bin/sh)&lt;/li&gt;&lt;li&gt;Doom&lt;/li&gt;&lt;li&gt;gzip&lt;/li&gt;&lt;li&gt;less (pager)&lt;/li&gt;&lt;li&gt;lok (/bin/awk)&lt;/li&gt;&lt;li&gt;lolcat&lt;/li&gt;&lt;li&gt;mandoc (man pages)&lt;/li&gt;&lt;li&gt;sbase (core utils)&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;tcc (C compiler)&lt;/li&gt;&lt;li&gt;Vim 5.7&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The libc is derived from musl libc and contains numerous modifications to suit Bunnix’s needs. The curses library is based on netbsd-curses.&lt;/p&gt;&lt;p&gt;The system works but it’s pretty buggy and some parts of it are quite slapdash: your milage will vary. Be prepared for it to crash!&lt;/p&gt;&lt;h2&gt;How Bunnix came together&lt;/h2&gt;&lt;p&gt;I started documenting the process on Mastodon on day 3 – check out &lt;a href=&quot;https://fosstodon.org/@drewdevault/112319697309218275&quot; target=&quot;_blank&quot;&gt;the Mastodon thread&lt;/a&gt; for the full story. Here’s what it looked like on day 3:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/bunnix.jpg&quot;&gt;
&lt;figcaption&gt;Screenshot of an early Bunnix build, which boots up, sets up available memory, and exercises an early in-memory filesystem&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Here’s some thoughts after the fact.&lt;/p&gt;&lt;p&gt;Some of Bunnix’s code stems from an earlier project, &lt;a href=&quot;https://sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Helios&lt;/a&gt;. This includes portions of the kernel which are responsible for some relatively generic CPU setup (GDT, IDT, etc), and some drivers like AHCI were adapted for the Bunnix system. I admit that it would probably not have been possible to build Bunnix so quickly without prior experience through Helios.&lt;/p&gt;&lt;p&gt;Two of the more challenging aspects were ext4 support and the virtual terminal, for which I brought in two external dependencies, lwext4 and libvterm. Both proved to be challenging integrations. I had to rewrite my filesystem layer a few times, and it’s still buggy today, but getting a proper Unix filesystem design (including openat and good handling of inodes) requires digging into lwext4 internals a bit more than I’d have liked. I also learned a lot about mixing source languages into a Hare project, since the kernel links together Hare, assembly, and C sources – it works remarkably well but there are some pain points I noticed, particularly with respect to building the ABI integration riggings. It’d be nice to automate conversion of C headers into Hare forward declaration modules. Some of this work already exists in hare-c, but has a ways to go. If I were to start again, I would probably be more careful in my design of the filesystem layer.&lt;/p&gt;&lt;p&gt;Getting the terminal right was difficult as well. I wasn’t sure that I was going to add one at all, but I eventually decided that I wanted to port vim and that was that. libvterm is a great terminal state machine library, but it’s poorly documented and required a lot of fine-tuning to integrate just right. I also ended up spending a lot of time on performance to make sure that the terminal worked smoothly.&lt;/p&gt;&lt;p&gt;Another difficult part to get right was the scheduler. Helios has a simpler scheduler than Bunnix, and while I initially based the Bunnix scheduler on Helios I had to throw out and rewrite quite a lot of it. Both Helios and Bunnix are single-CPU systems, but unlike Helios, Bunnix allows context switching within the kernel – in fact, even preemptive task switching enters and exits via the kernel. This necessitates multiple kernel stacks and a different approach to task switching. However, the advantages are numerous, one of which being that implementing blocking operations like disk reads and pipe(2) are much simpler with wait queues. With a robust enough scheduler, the rest of the kernel and its drivers come together pretty easily.&lt;/p&gt;&lt;p&gt;Another source of frustration was signals, of course. Helios does not attempt to be a Unix and gets away without these, but to build a Unix, I needed to implement signals, big messy hack though they may be. The signal implementation which ended up in Bunnix is pretty bare-bones: I mostly made sure that SIGCHLD worked correctly so that I could port dash.&lt;/p&gt;&lt;p&gt;Porting third-party software was relatively easy thanks to basing my libc on musl libc. I imported large swaths of musl into my own libc and adapted it to run on Bunnix, which gave me a pretty comprehensive and reliable C library pretty fast. With this in place, porting third-party software was a breeze, and most of the software that’s included was built with minimal patching.&lt;/p&gt;&lt;h2&gt;What I learned&lt;/h2&gt;&lt;p&gt;Bunnix was an interesting project to work on. My other project, Helios, is a microkernel design that’s Not Unix, while Bunnix is a monolithic kernel that is much, much closer to Unix.&lt;/p&gt;&lt;p&gt;One thing I was surprised to learn a lot about is filesystems. Helios, as a microkernel, spreads the filesystem implementation across many drivers running in many separate processes. This works well enough, but one thing I discovered is that it’s quite important to have caching in the filesystem layer, even if only to track living objects. When I revisit Helios, I will have a lot of work to do refactoring (or even rewriting) the filesystem code to this end.&lt;/p&gt;&lt;p&gt;The approach to drivers is also, naturally, much simpler in a monolithic kernel design, though I’m not entirely pleased with all of the stuff I heaped into ring 0. There might be room for an improved Helios scheduler design that incorporates some of the desirable control flow elements from the monolithic design into a microkernel system.&lt;/p&gt;&lt;p&gt;I also finally learned how signals work from top to bottom, and boy is it ugly. I’ve always felt that this was one of the weakest points in the design of Unix and this project did nothing to disabuse me of that notion.&lt;/p&gt;&lt;p&gt;I had also tried to avoid using a bitmap allocator in Helios, and generally memory management in Helios is a bit fussy altogether – one of the biggest pain points with the system right now. However, Bunnix uses a simple bitmap allocator for all conventional pages on the system and I found that it works really, really well and does not have nearly as much overhead as I had feared it would. I will almost certainly take those lessons back to Helios.&lt;/p&gt;&lt;p&gt;Finally, I’m quite sure that putting together Bunnix in just 30 days is a feat which would not have been possible with a microkernel design. At the end of the day, monolithic kernels are just much simpler to implement. The advantages of a microkernel design are compelling, however – perhaps a better answer lies in a hybrid kernel.&lt;/p&gt;&lt;h2&gt;What’s next&lt;/h2&gt;&lt;p&gt;Bunnix was (note the past tense) a project that I wrote for the purpose of recreational programming, so it’s purpose is to be fun to work on. And I’ve had my fun! At this point I don’t feel the need to invest more time and energy into it, though it would definitely benefit from some. In the future I may spend a few days on it here and there, and I would be happy to integrate improvements from the community – send patches to my &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/public-inbox&quot; target=&quot;_blank&quot;&gt;public inbox&lt;/a&gt;. But for the most part it is an art project which is now more-or-less complete.&lt;/p&gt;&lt;p&gt;My next steps in OS development will be a return to Helios with a lot of lessons learned and some major redesigns in the pipeline. But I still think that Bunnix is a fun and interesting OS in its own right, in no small part due to its demonstration of Hare as a great language for kernel hacking. Some of the priorities for improvements include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A directory cache for the filesystem and better caching generally&lt;/li&gt;&lt;li&gt;Ironing out ext4 bugs&lt;/li&gt;&lt;li&gt;procfs and top&lt;/li&gt;&lt;li&gt;mmaping files&lt;/li&gt;&lt;li&gt;More signals (e.g. SIGSEGV)&lt;/li&gt;&lt;li&gt;Multi-user support&lt;/li&gt;&lt;li&gt;NVMe block devices&lt;/li&gt;&lt;li&gt;IDE block devices&lt;/li&gt;&lt;li&gt;ATAPI and ISO 9660 support&lt;/li&gt;&lt;li&gt;Intel HD audio support&lt;/li&gt;&lt;li&gt;Network stack&lt;/li&gt;&lt;li&gt;Hare toolchain in the base system&lt;/li&gt;&lt;li&gt;Self hosting&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Whether or not it’s me or one of you readers who will work on these first remains to be seen.&lt;/p&gt;&lt;p&gt;In any case, have fun playing with Bunnix!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Bunnix/</link>
        
        <pubDate>Fri, 24 May 2024 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Bunnix/</guid>
      </item>
    
      <item>
        
        
          <title>Copyleft licenses are not “restrictive”</title>
          <description>
            &lt;p&gt;One may observe an axis, or a “spectrum”, along which free and open source software licenses can be organized, where one end is “permissive” and the other end is “copyleft”. It is important to acknowledge, however, that though copyleft can be found at the opposite end of an axis with respect to permissive, it is not synonymous with the linguistic antonym of permissive – that is, copyleft licenses are not “restrictive” by comparison with permissive licenses.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Aside: Free software is not synonymous with copyleft and open source is not synonymous with permissive, though this is a common misconception. Permissive licenses are generally free software and copyleft licenses are generally open source; the distinction between permissive and copyleft is orthogonal to the distinction between free software and open source.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;It is a common misunderstanding to construe copyleft licenses as more “restrictive” or “less free” than permissive licenses. This view is predicated on a shallow understanding of freedom, a sort of passive freedom that presents as the absence of obligations. Copyleft is predicated on a deeper understanding of freedom in which freedom is a &lt;em&gt;positive guarantee of rights&lt;/em&gt;.&lt;sup&gt;&lt;a
href=&quot;https://plato.stanford.edu/entries/liberty-positive-negative/&quot;&gt;[source]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Let’s consider the matter of freedom, obligation, rights, and restrictions in depth.&lt;/p&gt;&lt;p&gt;Both forms of licenses include obligations, which are not the same thing as restrictions. An example of an obligation can be found in the permissive MIT license:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Permission is hereby granted […] to deal in the Software without restriction […] subject to the following conditions:&lt;/p&gt;&lt;p&gt;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This obliges the user, when distributing copies of the software, to include the copyright notice. However, it does not &lt;em&gt;restrict&lt;/em&gt; the use of the software under any conditions. An example of a restriction comes from the infamous JSON license, which adds the following clause to a stock MIT license:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The Software shall be used for Good, not Evil.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;IBM famously petitioned Douglas Crockford for, and received, a license to do evil with JSON.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This kind of clause is broadly referred to in the free software jargon as “discrimination against field of endeavour”, and such restrictions contravene both the free software and open source definitions. To quote the &lt;a href=&quot;https://opensource.org/osd&quot; target=&quot;_blank&quot;&gt;Open Source Definition&lt;/a&gt;, clause 6:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The license must not restrict anyone from making use of the program in a specific field of endeavor. For example, it may not restrict the program from being used in a business, or from being used for genetic research.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;No such restrictions are found in free or open source software licenses, be they permissive or copyleft – all FOSS licenses permit the use of the software for any purpose without restriction. You can sell both permissive and copyleft software, use it as part of a commercial cloud service,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; use the software as part of a nuclear weapons program,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; or do whatever else you want with it. There are no restrictions on how free software is used, regardless of if it is permissive or copyleft.&lt;/p&gt;&lt;p&gt;Copyleft does not impose restrictions, but it does impose obligations. The obligations exist to guarantee rights to the users of the software – in other words, to ensure freedoms. In this respect copyleft licenses are &lt;em&gt;more free&lt;/em&gt; than permissive licenses.&lt;/p&gt;&lt;p&gt;Freedom is a political concept, and in order to understand this, we must consider it in political terms, which is to say as an exercise in power dynamics. Freedom without obligation is a contradiction. Freedom &lt;em&gt;emerges&lt;/em&gt; from obligations, specifically obligations imposed on power.&lt;/p&gt;&lt;p&gt;Where does freedom come from?&lt;/p&gt;&lt;p&gt;Consider the United States as an example, a society which sets forth freedom as a core political value.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; Freedoms in the US are ultimately grounded in the US constitution and its bill of rights. These tools create freedoms by guaranteeing rights to US citizens through the imposition of &lt;em&gt;obligations&lt;/em&gt; on the government. For instance, you have a right to an attorney when accused of a crime in the United States, and as such the government is &lt;em&gt;obliged&lt;/em&gt; to provide you with one. It is from obligations such as these that freedom emerges. Freedom of assembly, another example, is guaranteed such that the police are prevented from breaking up peaceful protests – this freedom emerges from a &lt;em&gt;constraint&lt;/em&gt; (or restriction, if you must) on power (the government) as a means of guaranteeing the rights and freedom of those with less power by comparison (its citizens).&lt;/p&gt;&lt;p&gt;Who holds the power in the context of software?&lt;/p&gt;&lt;p&gt;Consider non-free software by contrast: software is written by corporations and sold on to users with substantial restrictions on its use. Corporations hold more power than individuals: they have more resources (e.g. money), more influence, and, in a sense more fundamental to the software itself, they retain in private the tools to understand the software, or to modify its behavior, and they dictate the conditions under which it may be used (e.g. only if your license key has not expired, or only for certain purposes). This is true of anyone who retains the source code in private and uses copyright law to enforce their will upon the software – in this way they possess, and exercise, power over the user.&lt;/p&gt;&lt;p&gt;Permissive licenses do not provide any checks on this power; generally they preserve &lt;a href=&quot;https://en.wikipedia.org/wiki/Moral_rights&quot; target=&quot;_blank&quot;&gt;moral rights&lt;/a&gt; and little else. Permissive licenses provide for relatively few and narrow freedoms, and are not particularly “free” as such. Copyleft licenses constrain these powers through additional obligations, and from these obligations greater freedoms emerge. Specifically, they oblige reciprocity. They are distinguished from permissive licenses in this manner, but where permissive licenses &lt;em&gt;permit&lt;/em&gt;, copyleft does not &lt;em&gt;restrict&lt;/em&gt; per-se – better terms might be “reciprocal” and “non-reciprocal”, but perhaps that ship has sailed. “You may use this software &lt;em&gt;if&lt;/em&gt; …” is a statement made both by permissive and copyleft licenses, with different &lt;em&gt;if&lt;/em&gt;s. Neither form of license says “you cannot use this software &lt;em&gt;if&lt;/em&gt; …”; licenses which do so are non-free.&lt;/p&gt;&lt;p&gt;Permissive licenses and copyleft licenses are both free software, but only the latter provides a guarantee of rights, and while both might be free only the latter provides &lt;em&gt;freedom&lt;/em&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Copyleft-is-not-restrictive/</link>
        
        <pubDate>Fri, 19 Apr 2024 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Copyleft-is-not-restrictive/</guid>
      </item>
    
      <item>
        
        
          <title>FDO&apos;s conduct enforcement actions regarding Vaxry</title>
          <description>
            &lt;p&gt;freedesktop(.org), aka FDO, recently banned Hyprland maintainer Vaxry from the FDO community, and in response Vaxry has taken his case to the court of public opinion, publishing their email exchanges and writing about it on his blog.&lt;/p&gt;&lt;p&gt;It saddens me to bear witness to these events today. I wrote &lt;a href=&quot;https://drewdevault.com/2023/09/17/Hyprland-toxicity.html&quot; target=&quot;_blank&quot;&gt;in September of last year&lt;/a&gt; about problems with toxicity in the Hyprland community. I initially reached out to Vaxry to discuss these problems in private in February of last year. I failed to get through to him, leading to that blog post in September. I spent some time in the following weeks talking with Vaxry on his behavior and his community’s social norms, again in private, but again, I was unable to get through to him. Unfortunately, we find ourselves again leaving the private sphere and discussing Vaxry’s behavior and the problem posed by the Hyprland community once again.&lt;/p&gt;&lt;p&gt;The fact of the matter is that Hyprland remains a toxic community, enabled and encouraged by its toxic leadership, namely Vaxry. FDO’s decision to ban Vaxry is ultimately a consequence of Vaxry’s behavior, and because he has elected to appeal his case in public, I am compelled to address his behavior in public. I hereby rise firmly in defense of FDO’s decision.&lt;/p&gt;&lt;p&gt;I invite you to start by reading the two email threads, &lt;a href=&quot;https://web.archive.org/web/20250306212807/https://blog.vaxry.net/resource/articleFDO/RHMails.pdf&quot; target=&quot;_blank&quot;&gt;one&lt;/a&gt;, and &lt;a href=&quot;https://web.archive.org/web/20240412194128/https://blog.vaxry.net/resource/articleFDO/lyudeReply.pdf&quot; target=&quot;_blank&quot;&gt;two&lt;/a&gt;, which Vaxry has published for your consideration, as well as Vaxry’s follow-ups on his blog, &lt;a href=&quot;https://blog.vaxry.net/articles/2024-fdo-and-redhat&quot; target=&quot;_blank&quot;&gt;one&lt;/a&gt;, and &lt;a href=&quot;https://blog.vaxry.net/articles/2024-fdo-and-redhat2&quot; target=&quot;_blank&quot;&gt;two&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Here’s my read on the situation.&lt;/p&gt;&lt;p&gt;The FDO officer that reached out to Vaxry did it after Vaxry’s problematic behavior was brought to her attention by members of the FDO community, and was acting on her mandate within the FDO conduct enforcement board by investigating complaints submitted to her by this community. It is not a stretch to suggest a close relationship between these communities exists: FDO is the steward of both the Wayland protocol and implementation and the wlroots library, essential dependencies of Hyprland and sources for collaboration between Hyprland and FDO. Vaxry and other members of the Hyprland community had already participated extensively in these projects (mainly in discussions on IRC and GitLab issues) at the time of the email exchange, in spaces where the code of conduct applies.&lt;/p&gt;&lt;p&gt;The FDO officer duly investigated the complaints she had received and found, in collaboration with the other members of the FDO conduct enforcement team, that they were credible, and worrying. There are numerous examples of behavior from Vaxry that contravenes the FDO code of conduct in several different respects, and any number of them would be grounds for an immediate ban. Since these behaviors are concerning, but did not take place in the FDO community, the conduct board decided to issue a warning in private, stating that if this sort of behavior was seen in the FDO community that it would result in enforcement action from the conduct team.&lt;/p&gt;&lt;p&gt;All of the actions from the FDO conduct team are reasonable and show considerable restraint. Vaxry could have taken it in stride with no consequences to himself. Instead, he immediately escalated the situation. He construes the FDO officer’s polite and well-reasoned warning as threats and intimidation. He minimizes examples of his own hate speech by shrugging them off as a joke. He belittles the FDO officer and builds a straw man wherein her email is an official statement on behalf of RedHat, and cites a conspiracy theory about &lt;abbr title=&quot;diversity, equity, and inclusion&quot;&gt;DEI&lt;/abbr&gt; programs at RedHat as justification for calling the FDO officer a hypocrite. He is insulted on my behalf that my name was cited in the FDO officer’s email in lowercase, “drew”, and feels the need to address this.&lt;/p&gt;&lt;p&gt;The FDO officer responds to Vaxry’s unhinged rant with a sarcastic quip clarifying that it was indeed within the FDO conduct team’s remit to ban Vaxry from their GitLab instance – I confess that in my view this was somewhat unprofessional, though I can easily sympathize with the FDO officer given the context. Following this, Vaxry states that Hyprland will cease all communication with FDO’s conduct team and &lt;em&gt;ignore&lt;/em&gt; (emphasis his) any future emails from them. Finally, he threatens legal action (on what basis is unclear) and signs the email.&lt;/p&gt;&lt;p&gt;Regardless of how you feel about the conduct team issuing a private warning to Vaxry on the basis of activities outside of FDO community spaces, the email thread that ensues most certainly is within the scope of the FDO code of conduct, and Vaxry’s behavior therein is sufficient justification for a ban from the FDO community as far as I’m concerned. The conduct team cites Vaxry’s stated intention to ignore any future conduct interventions as the ultimate reason for the ban, which I find entirely reasonable on FDO’s part. I have banned people for far less than this, and I stand by it.&lt;/p&gt;&lt;p&gt;Vaxry’s follow-up blog posts only serve to underscore this point. First of all, he immediately opens with a dog-whistle calling for the reader to harass the FDO officer in question: “I don’t condone harassing this person, but here is their full name, employer and contact details”:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I do not condone any hateful messages sent towards any of the parties mentioned.&lt;/p&gt;&lt;p&gt;Recently I have received an email filled with threats to my inbox, from a member of the X.org board, Freedesktop.org, and a Red Hat employee. Their name is [redacted].&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Moreover, Vaxry claims to have apologised for his past conduct, which is not true. In lieu of an apology, Vaxry has spent the “1.5 years” since the last incident posting angry rants on his blog calling out minority representation and “social justice warriors” in light of his perceived persecution. Meanwhile the Hyprland community remains a toxic place, welcoming hate, bullying, and harassment, but now prohibiting all “political” speech, which in practice means any discussion of LGBTQ topics, though this is largely unenforced. In the end, the Hyprland community’s fundamental problem is that they’re all “just having fun”, and it seems that they can’t have “fun” unless it’s at someone else’s expense.&lt;/p&gt;&lt;p&gt;The FDO team is right that Hyprland’s community reflects poorly on the Linux desktop community as a whole. Vaxry has created a foothold for hate, transphobia, homophobia, bullying, and harassment in the Linux desktop community. We are right to take action to correct this problem.&lt;/p&gt;&lt;p&gt;Every option other than banning Vaxry has been exhausted over the past year and a half. I personally spent several weeks following my last blog post on the matter discussing Vaxry’s behavior in confidence and helping him understand how to improve, and at my suggestion he joined a private community of positive male role models to discuss these issues in a private and empathetic space. After a few weeks of these private discussions, the last thing he said to me was “I do believe there could be arguments to sway my opinion towards genocide”.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;There’s nothing left to do but to build a fence around Hyprland and protect the rest of the community from them. I know that there’s a lot of good people who use and contribute to Hyprland, and I’m sorry for those of you who are affected by this problem. But, in the end, actions have consequences. The rest of the community has no choice but to sanction Vaxry.&lt;/p&gt;&lt;p&gt;And, to Vaxry – I know you’re reading this – there are going to continue to be consequences for your actions, but it’s still not too late to change. I know it’s humiliating to be called out like this, and I really would rather not have had to do so. FDO is probably not the last time you’re going to be banned if you don’t change course, and it would reflect better on you if you took it on the chin and didn’t post inflammatory rants on your blog – trust me, you don’t look like the good guy here. You are trapped in an echo chamber of hate, anger, and bigotry. I hope that you find a way out, and that someday you can build a community which is as great as your software is.&lt;/p&gt;&lt;p&gt;And, to the FDO officer in question: I’m so sorry that you’re at the ass end of all of this hate and abuse. You don’t deserve any of it. You did a good job, and I’m proud of you and the rest of the FDO conduct team. If you need any support, someone to talk to, don’t hesitate to reach out and ask, on IRC, Matrix, email, whatever. Don’t read the comments.&lt;/p&gt;&lt;p&gt;And on that note, I condemn in the harshest terms the response from communities like /r/linux on the subject. The vile harassment and hate directed at the FDO officer in question is obscene and completely unjustifiable. I don’t care what window manager or desktop environment you use – this kind of behavior is completely uncalled for. I expect better.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;P.S. The Hyprland community has already descended on me before even publishing this post, after I called Vaxry out on Mastodon a few hours ago. My notifications are not full of reasonable objections to my complaints, but instead the response is slurs and death threats. This only serves to prove my characterization of the Hyprland community as deeply toxic.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/FDO-conduct-enforcement/</link>
        
        <pubDate>Tue, 09 Apr 2024 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/FDO-conduct-enforcement/</guid>
      </item>
    
      <item>
        
        
          <title>Why Prusa is floundering, and how you can avoid their fate</title>
          <description>
            &lt;p&gt;Prusa is a 3D printer manufacturer which has a long history of being admired by the 3D printing community for high quality, open source printers. They have been struggling as of late, and came under criticism for making the firmware of their Mk4 printer non-free.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://lucumr.pocoo.org/2023/12/25/life-and-death-of-open-source/&quot; target=&quot;_blank&quot;&gt;Armin Ronacher&lt;/a&gt; uses Prusa as a case-study in why open source companies fail, and uses this example to underline his argument that open source needs to adapt for commercial needs, namely by adding commercial exclusivity clauses to its licenses – Armin is one of the principal proponents of the non-free Functional Source License. Armin cites his experience with a Chinese manufactured 3D printer as evidence that intellectual property is at the heart of Prusa’s decline, and goes on to discuss how this dynamic applies to his own work in developing a non-free license for use with Sentry. I find this work pretty interesting – FSL is a novel entry into the non-free license compendium, and it’s certainly a better way to do software than proprietary models, assuming that it’s not characterized as free or open source. But, allow me to use the same case study to draw different conclusions.&lt;/p&gt;&lt;p&gt;It is clear on the face of it that Prusa’s move to a non-free firmware is unrelated to their struggles with the Chinese competition – their firmware was GPL’d, and the cited competitor (Bambu) evidently respects copyleft, and there’s no evidence that Bambu’s printers incorporate derivatives of Prusa’s firmware in a manner which violates the GPL. Making the license non-free is immaterial to the market dynamics between Prusa and Bambu, so the real explanation must lie elsewhere.&lt;/p&gt;&lt;p&gt;If you had asked me 10 years ago what I expected Prusa’s largest risk would be, I would have simply answered “China” and you would have probably said the same. The Chinese economy and industrial base can outcompete Western manufacturing in almost every manufacturing market.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; This was always the obvious vulnerability in their business model, and they &lt;em&gt;absolutely&lt;/em&gt; needed to be prepared for this situation, or their death was all but certain. Prusa made one of the classic errors in open source business models: they made their product, made it open source, sold it, and assumed that they were done working on their business model.&lt;/p&gt;&lt;p&gt;It was inevitable that someday Chinese manufacturers would undercut Prusa on manufacturing costs. Prusa responded to this certainty by not diversifying their business model whatsoever. There has only ever been one Prusa product: their latest 3D printer model. The Mk4 costs $1,200. You can buy the previous generation (at $1,000), or the MINI (from 2019, $500). You can open your wallet and get their high-end printers, which are neat but fail to address the one thing that most users at this price-point really want, which is more build volume. Or, you can buy an Ender 3 off Amazon right now for $180 and you’ll get better than half of the value of an Mk4 at an 85% discount. You could also buy Creality’s flagship model for a cool $800 and get a product which beats the Mk4 in every respect. China has joined the market, bringing with them all of the competitive advantages their industrial base can bring to bear, and Prusa’s naive strategy is causing their position to fall like a rock.&lt;/p&gt;&lt;p&gt;Someone new to 3D printing will pick up an Ender and will probably be happy with it for 1-2 years. When they upgrade, will they upgrade to a Prusa or an Ender 5? Three to five years a customer spends in someone else’s customer pipeline is an incredibly expensive opportunity cost Prusa is missing out on. This opportunity cost is the kind of arithmetic that would make loss leaders like a cheap, low-end, low-or-negative-margin Prusa printer make financial sense. Hell, Prusa should have made a separate product line of white-labeled Chinese entry-level 3D printers just to get people on the Prusa brand.&lt;/p&gt;&lt;p&gt;Prusa left many stones unturned. Bambu’s cloud slicer is a massive lost opportunity for Prusa. On-demand cloud printing services are another lost opportunity. Prusa could have built a marketplace for models &amp; parts and skimmed a margin off of the top, but they waited until 2022 to launch Printables – waiting until the 11th hour when everyone was fed up with Thingiverse. Imagine a Prusa where it works out of the box, you can fire up a slicer in your browser which auto-connects to your printer and prints models from a Prusa-operated model repository, paying $10 for a premium model, $1 off the top goes to Prusa, with the same saved payment details which ensure that a fresh spool of Prusa filament arrives at your front door when it auto-detects that your printer is almost out. The print you want is too big for your build volume? Click here to have it cloud printed – do you want priority shipping for that? Your hot-end is reaching the end of its life – as one of our valued business customers on our premium support contract we would be happy to send you a temporary replacement printer while yours is shipped in for service.&lt;/p&gt;&lt;p&gt;Prusa’s early foothold in the market was strong, and they were wise to execute the way they did early on. But they &lt;em&gt;absolutely&lt;/em&gt; had to diversify their lines of business. Prusa left gaping holes in the market and utterly failed to capitalize on any of them. Prusa could have been synonymous with 3D printing if they had invested in the brand (though they probably needed a better name). I should be able to walk into a Best Buy and pick up an entry-level Prusa for $250-$500, or into a Home Depot and pick up a workshop model for $1000-$2000. I should be able to bring it home, unbox it, scan a QR code to register it with PrusaConnect, and have a Benchy printing in less than 10 minutes.&lt;/p&gt;&lt;p&gt;Chinese manufacturers did all of this and more, and they’re winning. They aren’t just cheaper – they offer an outright better product. These are not cheap knock-offs: if you want the best 3D printer today it’s going to be a Chinese one, regardless of how much you want to spend, but, as it happens, you’re going to spend less.&lt;/p&gt;&lt;p&gt;Note that none of this is material to the license of the product, be it free or non-free. It’s about building a brand, developing a customer relationship, and identifying and exploiting market opportunities. Hackers and enthusiasts who found companies like Prusa tend to imagine that the product is everything, but it’s not. Maybe 10% of the work is developing the 3D printer itself – don’t abandon the other 90% of your business. Especially when you make that 10% open: someone else is going to repurpose it, do the other 90%, and eat your lunch. FOSS is &lt;em&gt;great&lt;/em&gt; precisely because it makes that 10% into community property and shares the cost of innovation, but you’d be a fool to act as if that was all there was to it. You need to deal with sales and marketing, chase down promising leads, identify and respond to risks, look for and exploit new market opportunities, and much more to be successful.&lt;/p&gt;&lt;p&gt;This is a classic failure mode of open source businesses, and it’s &lt;em&gt;Prusa’s fault&lt;/em&gt;. They had an excellent foothold early in the market, leveraging open source and open hardware to great results and working hand-in-hand with enthusiasts early on to develop the essential technology of 3D printing. Then, they figured they were done developing their business model, and completely dropped the ball as a result. Open source is not an “if you build it, the money will come” situation, and to think otherwise is a grave mistake. Businesses need to identify their risks and then mitigate them, and if they don’t do that due diligence, then it’s &lt;em&gt;their fault&lt;/em&gt; when it fails – it’s not a problem with FOSS.&lt;/p&gt;&lt;p&gt;Free and open source software is an incredibly powerful tool, including as a commercial opportunity. FOSS really has changed the world! But building a business is still hard, and in addition to its fantastic advantages, the FOSS model poses important and challenging constraints that you need to understand and work with. You have to be creative, and you must do a risk/reward assessment to understand how it applies to your business and how you can utilize it for commercial success. Do the legwork and you can utilize FOSS for a competitive advantage, but skip this step and you will probably fail within a decade.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Prusa-is-floundering/</link>
        
        <pubDate>Tue, 26 Dec 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Prusa-is-floundering/</guid>
      </item>
    
      <item>
        
        
          <title>Richard Stallman&apos;s political discourse on sex</title>
          <description>
            &lt;p&gt;Richard Stallman, the founder of the Free Software Foundation, has been subject to numerous allegations of misconduct. He stepped down in 2019, and following his re-instatement in 2021, a famous &lt;a href=&quot;https://rms-open-letter.github.io/&quot; target=&quot;_blank&quot;&gt;open letter&lt;/a&gt; was published in which numerous organizations and individuals from throughout the Free Software ecosystem called for his removal from the Free Software Foundation. The letter had no effect; Stallman remains a voting member of the FSF’s &lt;a href=&quot;https://www.fsf.org/about/staff-and-board&quot; target=&quot;_blank&quot;&gt;board of directors&lt;/a&gt; to this day and continues to receive numerous &lt;a href=&quot;https://stallman.org/talks.html&quot; target=&quot;_blank&quot;&gt;speaking engagements&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Content warning: This article discusses sexual abuse, sexual assault, sexual harassment, and all of the above with respect to minors, as well as the systemic normalization of abuse, and directly quotes statements which participate in the normalization of abuse.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This article presents an analysis of Stallman’s political discourse on sex with the aim of establishing the patterns that cause the sort of discomfort that led to Stallman’s public condemnation. In particular, we will address how Stallman speaks about sexual assault, harassment, consent, and minors in his discourse.&lt;/p&gt;&lt;p&gt;I think that it is important to acknowledge this behavior not as a series of isolated incidents, nor a conflict with Stallman’s “&lt;a href=&quot;https://www.fsf.org/news/statement-of-fsf-board-on-election-of-richard-stallman&quot; target=&quot;_blank&quot;&gt;personal style&lt;/a&gt;”, but a pattern of behavior from which a political narrative forms, and draws attention to the fact that the meager retractions, excuses, and non-apologies from both Stallman and the Free Software Foundation as a whole fail to account for that pattern in a meaningful way.&lt;/p&gt;&lt;p&gt;The failure of the Free Software community to account for Richard Stallman’s behavior has a chilling effect. The norms set by our leadership influence the norms of our broader community, and many members of the Free Software community look to Stallman as a ideological and political leader. The norms Stallman endorses are harmful and deeply confronting and alienating to many people, in particular women and children. Should these norms be adopted by our movement, we risk creating a community which enables the exploitation of vulnerable people.&lt;/p&gt;&lt;p&gt;Let’s begin to address this by considering Stallman’s retraction of his comments in support of pedophilia. The following comment from Stallman in 2013 drew harsh criticism:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;There is little evidence to justify the widespread assumption that willing participation in pedophilia hurts children.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20210325014249/https://stallman.org/archives/2012-nov-feb.html#04_January_2013_(Pedophilia)&quot;&gt;stallman.org, 04 January 2013 “Pedophilia”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Following much of the criticism directed at Stallman, he had a number of “personal conversations” which reframed his views. Of the many comments Stallman has made which drew ire, this is one of the few for which a correction was made, in September 2019:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Many years ago I posted that I could not see anything wrong about sex between an adult and a child, if the child accepted it.&lt;/p&gt;&lt;p&gt;Through personal conversations in recent years, I’ve learned to understand how sex with a child can harm per psychologically. This changed my mind about the matter: I think adults should not do that. I am grateful for the conversations that enabled me to understand why.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20210325015259/https://stallman.org/archives/2019-jul-oct.html#14_September_2019_(Sex_between_an_adult_and_a_child_is_wrong)&quot;&gt;stallman.org, 14 September 2019 “Sex between an adult and a child is wrong”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;This statement from Stallman has been accepted by his defenders as evidence of his capitulation on pedophilia. I argue that this statement is misleading due to the particular way Stallman uses the word “child”. When Stallman uses this word, he does so with a very specific meaning, which he explains on his website:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Children: Humans up to age 12 or 13 are children. After that, they become adolescents or teenagers. Let’s resist the practice of infantilizing teenagers, by not calling them “children”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://www.stallman.org/antiglossary.html&quot;&gt;stallman.org, “Anti-glossary”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;It seems clear from this definition is that Stallman’s comments are not a capitulation at all. His 2019 retraction, when interpreted using his definition of “children”, does not contradict most of Stallman’s past statements regarding sex and minors, including his widely criticized defenses of many people accused of sexual impropriety with minors.&lt;/p&gt;&lt;p&gt;Stallman’s most recent direct response to his criticism underscores this:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It was right for me to talk about the injustice to Minsky, but it was tone-deaf that I didn’t acknowledge as context the injustice that Epstein did to women or the pain that caused.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://www.fsf.org/news/rms-addresses-the-free-software-community&quot;&gt;fsf.org, April 12, 2021, “RMS addresses the free software community”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Stallman qualifies his apology by explicitly re-affirming his defense of Marvin Minsky, which is addressed in detail later in this piece. Stallman’s doubling-down here is consistent with the supposition that Stallman maintains the view that minors can have sexual relationships with adults of any age, provided that they aren’t “children” – in other words, provided they’re at least 13 or 14 years old.&lt;/p&gt;&lt;p&gt;Stallman cares deeply about language and its usage. His strange and deliberate usage of the word “children” is also found many times throughout his political notes over the years. For example:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It sounds horrible: “UN peacekeepers accused of child rape in South Sudan.” But the article makes it pretty clear that the “children” involved were not children. They were teenagers.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20180509120046/https://stallman.org/archives/2018-mar-jun.html#30_April_2018_(UN_peacekeepers_in_South_Sudan)&quot;&gt;stallman.org, 30 April 2018 “UN peacekeepers in South Sudan”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Here Stallman again explicitly distinguishes “teenagers” from children, drawing this distinction especially in the context of sexual relationships between adults and minors. Stallman repeats this pattern many times over the years – we see it again in Stallman’s widely criticized defense of Cody Wilson:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Cody Wilson has been charged with hiring a “child” sex worker. Her age has not been announced, but I think she must surely be a teenager, not a child. Calling teenagers “children” in this context is a way of smearing people with normal sexual proclivities as “perverts”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20180924231708/https://stallman.org/archives/2018-jul-oct.html#23_September_2018_(Cody_Wilson)&quot;&gt;stallman.org, 23 September 2018 “Cody Wilson”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;And once more when defending Roy Moore:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Senate candidate Roy Moore tried to start dating/sexual relationships with teenagers some decades ago.&lt;/p&gt;&lt;p&gt;He tried to lead Ms Corfman step by step into sex, but he always respected “no” from her and his other dates. Thus, Moore does not deserve the exaggerated condemnation that he is receiving for this. As an example of exaggeration: one mailing referred to these teenagers as “children”, even the one that was 18 years old. Many teenagers are minors, but none of them are children.&lt;/p&gt;&lt;p&gt;The condemnation is surely sparked by the political motive of wanting to defeat Moore in the coming election, but it draws fuel from ageism and the fashion for overprotectiveness of “children”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20180104112431/https://www.stallman.org/archives/2017-nov-feb.html#27_November_2017_(Roy_Moore&apos;s_relationships)&quot;&gt;stallman.org, 27 November 2017 “Roy Moore’s relationships”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Ms. Corfman was 14 at the time Roy Moore is accused of initiating sexual contact with her; Moore was 32 at the time. Here we see an example of him re-iterating his definition of “children”, a distinction he draws especially to suggest that an adult having sex with a minor is socially acceptable.&lt;/p&gt;&lt;p&gt;Note that Stallman refers to Ms. Corfman as Moore’s “date”. Stallman’s use of this word is important: here he normalizes the possibility that a minor and an adult could engage in a healthy dating relationship. In this statement, Stallman cites an article which explains circumstances which do not resemble such a normalized dating experience: Moore isolated Corfman from her mother, drove her directly to his home, and initiated sexual contact there.&lt;/p&gt;&lt;p&gt;Note also that the use of the phrase “step by step” in this quotation is more commonly referred to as “grooming” in the discourse on child sexual exploitation.&lt;/p&gt;&lt;p&gt;Stallman reaches for similar reasoning in other political notes, such as the following:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A British woman is on trial for going to a park and inviting teenage boys to have sex with her there. Her husband acted as a lookout in case someone else passed by. One teenager allegedly visited her at her house repeatedly to have sex with her.&lt;/p&gt;&lt;p&gt;None of these acts would be wrong in any sense, provided they took precautions against spreading infections. The idea that adolescents (of whatever sex) need to be “protected” from sexual experience they wish to have is prudish ignorantism, and making that experience a crime is perverse.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20170612074722/http://stallman.org/archives/2017-mar-jun.html#26_May_2017_(Prudish_ignorantism)&quot;&gt;stallman.org, 26 May 2017, “Prudish ignorantism”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;The woman in question, aged 60, had sex with her husband, age 69, in a public space, and invited spectators as young as 11 to participate.&lt;/p&gt;&lt;p&gt;Stallman has also sought to normalize adult attraction to minors, literally describing it as “normal” in September 2018:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Calling teenagers “children” encourages treating teenagers as children, a harmful practice which retards their development into capable adults.&lt;/p&gt;&lt;p&gt;In this case, the effect of that mislabeling is to smear Wilson. It is rare, and considered perverse, for adults to be physically attracted to children. However, it is normal for adults to be physically attracted to adolescents. Since the claims about Wilson is the latter, it is wrong to present it as the former.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://www.stallman.org/archives/2018-sep-dec.html#23_September_2018_(Cody_Wilson)&quot;&gt;stallman.org, 23 September 2018, “Cody Wilson”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;One month prior, Stallman made a statement which similarly normalized adult attraction to minors, and suggests that acting on this attraction should be acceptable to society, likening opposition to this view to homosexual conversion therapy:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;This accords with the view that Stendhal reported in France in the 1800s, that a woman’s most beautiful years were from 16 to 20.&lt;/p&gt;&lt;p&gt;Although this attitude on men’s part is normal, the author still wants to present it as wrong or perverted, and implicitly demands men somehow control their attraction to direct it elsewhere. Which is as absurd, and as potentially oppressive, as claiming that homosexuals should control their attraction and direct it towards to the other sex. Will men be pressured to undergo “age conversion therapy” intended to brainwash them to feel attracted mainly to women of their own age?&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20180911075211/https://www.stallman.org/archives/2018-jul-oct.html#21_August_2018_(Age_and_attraction)&quot;&gt;stallman.org, 21 August 2018, “Age and attraction”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;A trend is thus clearly seen in Stallman’s regular political notes, over several years, wherein Stallman re-iterates his position that “adolescents” or “teenagers” are distinct from “children” for the purpose of having sex with adults, and normalizes and defends adult attraction to minors and adults who perform sexual acts with minors. We see this distinction of the two groups, children and adolescents, outlined again on his “anti-glossary”, which still published on his website today, albeit without the connotations of sex. His regular insistence on a definition of children which excludes adolescents serves such that his redaction of his controversial 2013 comment serves to redact none of the other widely-condemned comments he has made since.&lt;/p&gt;&lt;p&gt;Stallman has often written political notes when people accused of sexual impropriety, particularly with minors, appear in the news, or appear among Stallman’s social circle. Stallman’s comments generally downplay the abuse and manipulate language in a manner which benefits perpetrators of abuse. We see this downplaying in another example from 2019:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Should we accept stretching the terms “sexual abuse” and “molestation” to include looking without touching?&lt;/p&gt;&lt;p&gt;I do not accept it.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://www.stallman.org/archives/2019-may-aug.html#11_June_2019_(Stretching_meaning_of_terms)&quot;&gt;stallman.org, 11 June 2019 “Stretching meaning of terms”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Stallman is writing here in response to a news article outlining accusations of sexual misconduct directed at Ohio State athletics doctor Richard Strauss. Strauss was accused of groping at least 177 students between 1979 and 1997 during routine physical exams, accusations corroborated by at least 50 members of the athletic department staff.&lt;/p&gt;&lt;p&gt;In addition to Stallman’s regular fixation of the use of the word “children” with respect to sex, this political note also draws our attention to the next linguistic fixation of Stallman I want to question: the use of phrases like “sexual abuse” and “sexual assault”. The term “sexual assault” also appears in Stallman’s “Anti-glossary”:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Sexual assault: The term is applied to a broad range of actions, from rape on one end, to the least physical contact on the other, as well as everything in between. It acts as propaganda for treating them all the same. That would be wrong.&lt;/p&gt;&lt;p&gt;The term is further stretched to include sexual harassment, which does not refer to a single act, but rather to a series of acts that amounts to a form of gender bias. Gender bias is rightly prohibited in certain situations for the sake of equal opportunity, but that is a different issue.&lt;/p&gt;&lt;p&gt;I don’t think that rape should be treated the same as a momentary touch. People we accuse have a right to those distinctions, so I am careful not to use the term “sexual assault” to categorize the actions of any person on any specific occasion.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://www.stallman.org/antiglossary.html&quot;&gt;stallman.org, “Anti-glossary”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Stallman often fixates on the term “sexual assault” throughout his political notes. He feels that the term fails to distinguish between “grave” and “minor” crimes, as he illustrated in 2021:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;“Sexual assault” is so vague that it makes no sense as a charge. Because of that term, we can’t whether these journalists were accused of a grave crime or a minor one. However, the charge of espionage shows this is political persecution.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://stallman.org/archives/2021-jul-oct.html#21_July_2021_(Imprisonment_of_journalists)&quot;&gt;stallman.org, 21 July 2021, “Imprisonment of journalists”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;I would like to find out what kind of crimes Stallman feels the need to distinguish along this axis. His other political notes give us some hints, such as this one regarding Al Franken’s sexual misconduct scandal:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If it is true that he persistently pressured her to kiss him, on stage and off, if he stuck his tongue into her mouth despite her objections, that could well be sexual harassment. He should have accepted no for an answer the first time she said it. However, calling a kiss “sexual assault” is an exaggeration, an attempt to equate it to much graver acts, that are crimes.&lt;/p&gt;&lt;p&gt;The term “sexual assault” encourages that injustice, and I believe it has been popularized specifically with that intention. That is why I reject that term.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20190801201704/https://stallman.org/archives/2019-may-aug.html#30_July_2019_(Al_Franken)&quot;&gt;stallman.org, 30 July 2019, “Al Franken”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Stallman also wrote in 2020 to question the use of the phrase again:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;In the US, when thugs&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; rape people they say are suspects, it is rare to bring them to justice.&lt;/p&gt;&lt;p&gt;I object to describing any one crime as “sexual assault” because that is vague about the severity of the crime. This article often uses that term to refer to many crimes that differ in severity but raise the same issue. That may be a valid practice.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://stallman.org/notes/2020-jul-oct.html#12_August_2020_(When_thugs_rape_people_they_say_are_suspects)&quot;&gt;stallman.org, 12 August 2020, “When thugs rape people they say are suspects”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;In the article Stallman cites in this political note, various unwelcome sexual acts by the police are described, the least severe of which is probably molestation.&lt;/p&gt;&lt;p&gt;More alarmingly, Stallman addresses his views on the term “sexual assault” in this 2017 note, affording for the possibility that a 35-year-old man could have had consensual sex with an 11-year-old girl.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Jelani Maraj (who I had never heard of) could be imprisoned for a long time for “sexual assault”. What does that concretely mean?&lt;/p&gt;&lt;p&gt;Due to the vagueness of the term “sexual assault” together with the dishonest law that labels sex with adolescents as “rape” even if they are willing, we cannot tell from this article what sort of acts Maraj was found to have committed. So we can’t begin to judge whether those acts were wrong.&lt;/p&gt;&lt;p&gt;I see at least three possibilities. Perhaps those acts really constituted rape — it is a possibility. Or perhaps the two had sex willingly, but her parents freaked out and demanded prosecution. Or, intermediate between those two, perhaps he pressured her into having sex, or got her drunk.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://stallman.org/archives/2017-nov-feb.html#13_November_2017_(Jelani_Maraj)&quot;&gt;stallman.org, 13 November 2017, “Jelani Maraj”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Another article by Stallman does not explicitly refer to sexual assault, but does engage in a bizarre defense of a journalist who was fired for masturbating during a video conference. In this article Stallman fixates on questions such as whether or not the genitals being in view of the webcam was intentional or not, and suggests that masturbating on a video call would be acceptable should the genitals remain unseen.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The New Yorker’s unpublished note to staff was vague about its grounds for firing Toobin. Indeed, it did not even acknowledge that he had been fired. This is unfair, like convicting someone on unstated charges. Something didn’t meet its “standards of conduct”, but it won’t tell us what — we can only guess. What are the possibilities? Intentionally engaging in video-call sex as a side activity during a work meeting? If he had not made a mistake in keeping that out of view of the coworkers, why would it make a difference what the side activity was?&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://www.stallman.org/articles/toobin.html&quot;&gt;stallman.org, November 2020, “On the Firing of Jeffrey Toobin”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Finally, Stallman elaborated on his thoughts on the term most recently in October 2023. This note gives the clearest view of Stallman’s preferred distinction between various sexual crimes:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I warned that the stretchable term “sexual assault”, which extends from grave crimes such as rape through significant crimes such as groping and down to no clear lower bound, could be stretched to criminalize minor things, perhaps even stealing a kiss. Now this has happened.&lt;/p&gt;&lt;p&gt;What next? Will a pat on the arm or a hug be criminalized? There is no clear limit to how far this can go, when a group builds up enough outrage to push it.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://www.stallman.org/archives/2023-sep-dec.html#15_October_2023_(Sexual_assault_for_stealing_a_kiss)&quot;&gt;stallman.org, 15 October 2023, “Sexual assault for stealing a kiss”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;From Stallman’s statements, we can refine his objection to the term “sexual assault”, and sexual behaviors generally, to further suggest that the following beliefs are held by Stallman on the subject:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Groping and molestation are not sexual assault, but are crimes&lt;/li&gt;&lt;li&gt;Kissing someone without consent is not sexual assault, furthermore it is not wrong&lt;/li&gt;&lt;li&gt;Masturbating during a video conference is not wrong if you are not seen doing so&lt;/li&gt;&lt;li&gt;A 35-year-old man having sex with an 11-year-old girl does not constitute rape, nor sexual assault, but is in fact conscionable&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The last of these may be covered under Stallman’s 2019 retraction, even accounting for Stallman’s unconventional use of the word “children”.&lt;/p&gt;&lt;p&gt;Stallman’s fixation on the term “sexual assault” can be understood in his political notes as having the political aims of eroding the meaning of the phrase, questioning the boundaries of consent, downplaying the importance of agency in intimate interactions, appealing for the defense of people accused of sexual assault, and arguing for sexual relationships between minors and adults to be normalized. In one notable case, he has used this political angle to rise to the defense of his friends – in Stallman’s infamous email regarding Marvin Minsky, he writes the following:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The injustice [done to Minsky] is in the word “assaulting”. The term “sexual assault” is so vague and slippery that it facilitates accusation inflation: taking claims that someone did X and leading people to think of it as Y, which is much worse than X.&lt;/p&gt;&lt;p&gt;(…)&lt;/p&gt;&lt;p&gt;The word “assaulting” presumes that he applied force or violence, in some unspecified way, but the article itself says no such thing. Only that they had sex.&lt;/p&gt;&lt;p&gt;We can imagine many scenarios, but the most plausible scenario is that she presented herself to him as entirely willing. Assuming she was being coerced by Epstein, he would have had every reason to tell her to conceal that from most of his associates.&lt;/p&gt;&lt;p&gt;I’ve concluded from various examples of accusation inflation that it is absolutely wrong to use the term “sexual assault” in an accusation.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— Excerpt from &lt;a href=&quot;https://scribe.rip/medium.com/@selamie/remove-richard-stallman-fec6ec210794&quot;&gt;Selam G’s recount of Stallman’s email&lt;/a&gt; to MIT Computer Science and Artificial Intelligence Laboratory mailing list, September 2019. Selam’s quotation has been corroborated by other sources. Minsky is, in this context, accused of having had a sexual encounter with a minor facilitated by convicted child trafficker Ghislaine Maxwell. The original accusation does not state that this sexual encounter actually occurred; only that the minor in question was instructed to have sex with Minsky. Minsky would have been at least 75 years old at the time of the alleged incident; the minor was 16. &lt;/small&gt;&lt;/p&gt;&lt;p&gt;There is an important, but more subtle pattern in Stallman’s statements that I want to draw your attention to here: Stallman appears to have little to no understanding of the role of power dynamics in sexual harassment, assault, and rape. Stallman appears to reject the supposition that these acts could occur without an element of outwardly apparent violent coercion.&lt;/p&gt;&lt;p&gt;This is most obviously evidenced by his statements regarding the sexual abuse of minors; most people understand that minors cannot consent to sex even if they “appear willing”, in particular because an adult in this situation is exploiting a difference in experience and maturity to manipulate the child into sexually satisfying them – in other words, a power differential. Stallman seems to reject this understanding of consent in his various defenses of people accused of sexual impropriety with minors, and in cases where the pretense of consent cannot be easily established, he offers the perpetrator the benefit of the doubt.&lt;/p&gt;&lt;p&gt;We can also find an example of Stallman disregarding power dynamics with respect to adults in the following political note from 2017:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A famous theater director had a habit of pestering women, asking them for sex.&lt;/p&gt;&lt;p&gt;As far as I can tell from this article, he didn’t try to force women into sex.&lt;/p&gt;&lt;p&gt;When women persistently said no, he does not seem to have tried to punish them.&lt;/p&gt;&lt;p&gt;The most he did was ask.&lt;/p&gt;&lt;p&gt;He was a pest, but nothing worse than that.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;small&gt;— &lt;a href=&quot;https://web.archive.org/web/20180131020215/https://stallman.org/archives/2017-jul-oct.html#29_October_2017_(Pestering_women)&quot;&gt;stallman.org, 29 October 2017, “Pestering women”&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;In this case we have an example of “quid pro quo”, a kind of sexual harassment which weaponizes power dynamics for sexual gratification. This kind of sexual harassment is explicitly cited as illegal by Title VII of the US Civil Rights Act. A lack of competence in this respect displayed by Stallman, whose position in the Free Software Foundation board of directors requires that he act in a manner consistent with this law, is alarming.&lt;/p&gt;&lt;p&gt;I have identified this blindness to power dynamics as a recurring theme in Stallman’s comments on sexual abuse, be it with respect to sexual relationships between minors and adults, managers and subordinates, students and teachers, or public figures and their audience. I note for the reader that Stallman has held and currently holds several of these positions of power.&lt;/p&gt;&lt;p&gt;In addition to his position as a voting member of the Free Software Foundation’s Board of Directors, Stallman is still invited to speak at events and conferences. &lt;a href=&quot;https://github.com/ddol/rre-rms/blob/master/fulltext/20111018.txt&quot; target=&quot;_blank&quot;&gt;Stallman’s infamous rider&lt;/a&gt; prescribes a number of his requirements for attending an event; most of his conditions are relatively reasonable, though amusing. In this document, he states his preference for being accommodated in private, on a “spare couch”, when he travels. At these events, in these private homes, he may be afforded many opportunities to privacy with vulnerable people, including minors that, in his view, can consent to having sex with adults.&lt;/p&gt;&lt;p&gt;In summary, Stallman has a well-documented and oft-professed set of political beliefs which reject the social and legal norms regarding consent. He is not simply quietly misled in these beliefs; rather he advocates for these values using his political platform. He has issued no meaningful retractions of these positions or apologies for harm caused, and has continued to pursue a similar agenda since his return to the FSF board of directors.&lt;/p&gt;&lt;p&gt;This creates a toxic environment not only in the Free Software Foundation and in Stallman’s direct purview, but in the broader Free Software movement. The free software movement is culturally poisoned by our support of Stallman as our ideological leader. The open letter calling for Stallman’s removal received 3,000 signatures; the counter-letter in support of Stallman received 6,876 before it stopped accepting submissions.&lt;/p&gt;&lt;p&gt;Richard Stallman founded the Free Software Foundation in 1985, and has performed innumerable works to the benefit of our community since then. We’ve taken Stallman’s views on software freedom seriously, and they’ve led us to great achievements. It is to Stallman’s credit that the Free Software community is larger than one man. However, one’s political qualifications to speak about free software does not make one qualified to address matters of sex; in this respect Stallman’s persistence presents as dangerous incompetence.&lt;/p&gt;&lt;p&gt;When we consider his speech on sex as a discourse that has been crafted and rehearsed methodically over the years, he asks us to consider him seriously, and so we must. When we analyze the dangerous patterns in this discourse, we have to conclude that he is not fit for purpose in his leadership role, and we must acknowledge the shadow that our legitimization of his discourse casts on our community.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/RMS-on-sex/</link>
        
        <pubDate>Sat, 25 Nov 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/RMS-on-sex/</guid>
      </item>
    
      <item>
        
        
          <title>Can I be on your podcast?</title>
          <description>
            &lt;p&gt;I am working on rousing the &lt;a href=&quot;https://harelang.org&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt; community to get the word out about our work. I have drafted the &lt;a href=&quot;https://harelang.org/evangelism/&quot; target=&quot;_blank&quot;&gt;Hare evangelism&lt;/a&gt; guidelines to this effect, which summarizes how we want to see our community bringing Hare to more people.&lt;/p&gt;&lt;p&gt;We’d like to spread the word in a way which is respectful of the attention of others – we’re explicitly eschewing unsolicited prompts for projects to consider writing/rewriting in Hare, as well as any paid sponsorships or advertising. Blog posts about Hare, videos, participating in (organic) online discussions – much better! And one idea we have is to talk about Hare on podcasts which might be interested in the project.&lt;/p&gt;&lt;p&gt;If that describes your podcast, here’s my bold request: can I make an appearance?&lt;/p&gt;&lt;p&gt;Here are some mini “press kits” to give you a hook and some information that might be useful for preparing an interview.&lt;/p&gt;&lt;h2&gt;The Hare programming language&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;Hare is a systems programming language designed to be simple, stable, and robust. Hare uses a static type system, manual memory management, and a minimal runtime. It is well-suited to writing operating systems, system tools, compilers, networking software, and other low-level, high performance tasks.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Hare has been in development since late 2019 and today has about 100 contributors.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://harelang.org/&quot; target=&quot;_blank&quot;&gt;Official website&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/hare/&quot; target=&quot;_blank&quot;&gt;Source code &amp; development resources&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://spacepub.space/w/ajS983L4cEG82jiiaTYfXv&quot; target=&quot;_blank&quot;&gt;“Introducing the Hare programming language”, video, 2022&lt;/a&gt;&lt;/li&gt;&lt;li&gt;GPLv3, MPL 2.0, MIT&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://harelang.org/mascot.png&quot;&gt;
&lt;figcaption&gt;A hand-drawn picture of a rabbit&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Hare’s official mascot, Harriet. Drawn by Louis Taylor, CC-0&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;The Ares operating system&lt;/h2&gt;&lt;p&gt;Ares is an operating system written in Hare which is under development. It features a micro-kernel oriented design and runs on x86_64 and aarch64. Its design is inspired by the seL4 micro-kernel and Plan 9.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ares-os.org/&quot; target=&quot;_blank&quot;&gt;Official website&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Source code &amp; development resources&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://spacepub.space/w/wpKXfhqqr7FajEAf4B2Vc2&quot; target=&quot;_blank&quot;&gt;“Introducing the Helios micro-kernel”, video, FOSDEM 2023&lt;/a&gt;&lt;/li&gt;&lt;li&gt;GPLv3&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://files.catbox.moe/a4g9my.jpg&quot;&gt;
&lt;figcaption&gt;A photo of a laptop running the Ares operating system&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;A picture of a ThinkPad running Ares and demonstrating some features&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;Himitsu: a secret storage system&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;Himitsu is a secure secret storage system for Unix-like systems. It provides an arbitrary key/value store (where values may be secret) and a query language for manipulating the key store.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Himitsu is written in Hare.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://himitsustore.org/&quot; target=&quot;_blank&quot;&gt;Official website&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;Source code &amp; development resources&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://himitsustore.org/intro.mp4&quot; target=&quot;_blank&quot;&gt;Video tour&lt;/a&gt;&lt;/li&gt;&lt;li&gt;GPLv3&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Interested?&lt;/h2&gt;&lt;p&gt;If any of these topics are relevant for your podcast and you’d like to talk about them, please reach out to me via email: &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;drew@ddevault.org&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Thanks!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Can-I-be-on-your-podcast/</link>
        
        <pubDate>Thu, 09 Nov 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Can-I-be-on-your-podcast/</guid>
      </item>
    
      <item>
        
        
          <title>On &quot;real name&quot; policies</title>
          <description>
            &lt;p&gt;Some free software projects reject anonymous or pseudonymous contributions, requiring you to author patches using your “real name”. Such projects have a so-called “real name” policy; Linux is one well-known example.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The root motivations behind such policies vary, but in my experience the most often cited rationale is that it’s important to establish the provenance of the contribution for copyright reasons. In the case of Linux, contributors are asked to “sign-off” their commits to indicate their agreement to the terms of the Developer Certificate of Origin (DCO), which includes clauses like the following:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;To some extent, the DCO serves as a legal assertion of copyright and an agreement to license a work under given copyright terms (GPLv2 in the case of Linux). This record also means that the author of the code is accountable in case the copyright is challenged; in the case of an anonymous or pseudonymous contributor you’re shit out of luck. At that point, liability over the disagreement would likely fall into the hands of the maintainer that accepted the contribution. It is reasonable for a maintainer to ask a contributor to assert their copyright and accept liability over the provenance of their code in a legally meaningful and accountable form.&lt;/p&gt;&lt;p&gt;The possibility that someone may have something useful to offer to a free software project, but is not comfortable disclosing their name for any number of reasons, is a reasonable supposition. A maintainer whose “real name” policy is challenged on this basis would also be reasonable in saying “I feel for you, but I cannot agree to accept legal liability over the provenance of this code, nor can I communicate that risk to end-users who acquire code under a license that may or may not be valid as such”.&lt;/p&gt;&lt;p&gt;“Real name” policies are controversial in the free software community. I open with this perspective in an attempt to cool down the room. Those who feel marginalized by “real name” policies often skew young, and many treat matters such as copyright and licensing with disdain. Moreover, the problem tends to inflame deeply hurtful sentiments and raise thorny matters of identity and discrimination, and it’s easy to construe the intent of the policymakers as the intent to cause harm. The motivations behind these policies are reasonable.&lt;/p&gt;&lt;p&gt;That said, intent or otherwise, these policies can cause harm. The profile of the contributor who is comfortable using their “real name” is likely to fall more narrowly into over-represented demographics in our community; enforcing a real-name policy will ostracize some people. Those with marginalized identities tend to be less comfortable with disclosing their “real name”. Someone who has been subject to harassment may not be comfortable with this disclosure, since it offers more fuel to harassers keeping tabs on their activities. The use of a “real name” also confers a gender bias; avoiding a “real name” policy neatly eliminates discrimination on this basis. Of course, there are also many &lt;a href=&quot;https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/&quot; target=&quot;_blank&quot;&gt;falsehoods programmers believe about names&lt;/a&gt; which can present in the implementation of such a policy.&lt;/p&gt;&lt;p&gt;There is also one particular problem which has been at the heart of conflict surrounding the use of “real-name” policies in free software: transgender identities. A transgender person is likely to change their name in the process of assuming their new identity. When this happens, their real name changes. However, it may or may not match their legal name – some trans people opt to change it, others don’t; if they do it is a process that takes time. Meanwhile, addressing a trans person by their old name, or “deadname”, is highly uncomfortable. Doing so deliberately, as a matter of policy or otherwise, is a form of discrimination. Many trans people experience deliberate “deadnaming” as a form of harassment in their daily lives, and institutionalizing this behavior is cruel.&lt;/p&gt;&lt;p&gt;The truth is, managing the names of participants is more challenging than anyone would like. On the one hand, names establish accountability and facilitate collaboration, and importantly, credit the authors of a work for services performed. On the other hand, names are highly personal and deeply affecting, and their usage and changes over time are the subject of important consideration at the discretion of their owner. A complicating factor is that handling names properly introduces technical problems which must be overcome.&lt;/p&gt;&lt;p&gt;To embrace the advantages of “real name” policies – establishing provenance, encouraging accountability, fostering a social environment – without causing harm, the approach I have settled on for my projects is to use the DCO to establish provenance and encourage contributors to sign-off and participate under the identity they feel most comfortable with. I encourage people to utilize an identity they use beyond the project’s walls, to foster a social environment and a connection to the broader community, to establish accountability, and to ensure that participants are reachable for further discussion on their work. If a contributor’s identity changes, we make every effort to support this change in contemporary, future, and historical use.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/On-real-names/</link>
        
        <pubDate>Tue, 31 Oct 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/On-real-names/</guid>
      </item>
    
      <item>
        
        
          <title>Going off-script</title>
          <description>
            &lt;p&gt;There is a phenomenon in society which I find quite bizarre. Upon our entry to this mortal coil, we are endowed with self-awareness, agency, and free will. Each of the 8 billion members of this human race represents a unique person, a unique worldview, and a unique agency. Yet, many of us have the same fundamental goals and strive to live the same life.&lt;/p&gt;&lt;p&gt;I think of such a life experiences as “following the script”. Society lays down for us a framework for living out our lives. Everyone deviates from the script to some extent, but most people hit the important beats. In Western society, these beats are something like, go to school, go to college, get a degree, build a career, get married, have 1.5 children, retire to Florida, die.&lt;/p&gt;&lt;p&gt;There are a number of reasons that someone may deviate from the script. The most common case is that the deviations are imposed by circumstance. A queer person will face discrimination, for instance, in marriage, or in adopting and raising children. Someone born into the lower class will have reduced access to higher education and their opportunities for career-building are curtailed accordingly; similar experiences follow for people from marginalized groups. Furthermore, more and more people who might otherwise be able to follow the script are finding that they can’t afford a home and don’t have the resources to build a family.&lt;/p&gt;&lt;p&gt;There are nevertheless many people who are afforded the opportunity to follow the script, and when they do so, they often experience something resembling a happy and fulfilling life. Generally this is not the result of a deliberate choice – no one was presented with the script and asked “is this what you want”? Each day simply follows the last and you make the choices that correspond with what you were told a good life looks like, and sometimes a good life follows.&lt;/p&gt;&lt;p&gt;Of course, it is entirely valid to want the “scripted” life. But you were not asked if you wanted it: it was just handed to you on a platter. The average person lacks the philosophical background which underpins their worldview and lifestyle, and consequently cannot explain &lt;em&gt;why&lt;/em&gt; it’s “good”, for them or generally. Consider your career. You were told that it was a desirable thing to build for yourself, and you understand how to execute your duties as a member of the working class, but can you explain why those duties are important and why you should spend half of your waking life executing them? Of course, if you are good at following the script, you are rewarded for doing so, generally with money, but not necessarily with self-actualization.&lt;/p&gt;&lt;p&gt;This state of affairs leads to some complex conflicts. This approach to life favors the status quo and preserves existing power structures, which explains in part why it is re-enforced by education and broader social pressures. It also leads to a sense of learned helplessness, a sense that this is the only way things can be, which reduces the initiative to pursue social change – for example, by forming a union.&lt;/p&gt;&lt;p&gt;It can also be uncomfortable to encounter someone who does not follow the script, or even questions the script. You may be playing along, and mostly or entirely exposed to people who play along. Meeting someone who doesn’t – they skipped college, they don’t want kids, they practice polyamory, they identify as a gender other than what you presumed, etc – this creates a moment of dissonance and often resistance. This tends to re-enforce biases and can even present as inadvertent micro-aggressions.&lt;/p&gt;&lt;p&gt;I think it’s important to question the script, even if you decide that you like it. You should be able to explain &lt;em&gt;why&lt;/em&gt; you like it. This process of questioning is a radical act. A radical, in its non-pejorative usage, is born when someone questions their life and worldview, decides that they want something else, and seeks out others who came to similar conclusions. They organize, they examine their discomfort and put it to words, and they share these words in the hope that they can explain a similar discomfort that others might feel within themselves. Radical movements, which by definition is any movement which challenges the status quo, are the stories of the birth and spread of radical ideas.&lt;/p&gt;&lt;p&gt;Ask yourself: who are you? Did you choose to be this person? Who do you want to be, and how will you become that person? Should you change your major? Drop out? Quit your job, start a business, found a labor union? Pick up a new hobby? Join or establish a social club? An activist group? Get a less demanding job, move into a smaller apartment, and spend more time writing or making art? However you choose to live, choose it deliberately.&lt;/p&gt;&lt;p&gt;The next step is an exercise in solidarity. How do you feel about others who made their own choices, choices which may be alike or different to your own? Or those whose choices were constrained by their circumstances? What can you do together that you couldn’t do alone?&lt;/p&gt;&lt;p&gt;Who do you want to be? Do you know?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Going-off-script/</link>
        
        <pubDate>Fri, 13 Oct 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Going-off-script/</guid>
      </item>
    
      <item>
        
        
          <title>The forbidden topics</title>
          <description>
            &lt;p&gt;There are forbidden topics in the hacker community. One is sternly reprimanded for bringing them up, by their peers, their leaders, and the community at large. In private, one can expect threats and intimidation; in public, outcry and censorship. The forbidden topics are enforced by the moderators of our spaces, taken off of forums, purged from chat rooms, and cleaned up from GitHub issues and mailing lists; the ban-hammers fall swiftly and resolutely. My last article to touch these subjects was removed from Hacker News by the moderators within 30 minutes and landed several death threats in my inbox. The forbidden topics, when raised, are met with a resounding, aggressive dismissal and unconditional condemnation.&lt;/p&gt;&lt;p&gt;Some years ago, the hacker community possessed near-unanimous praise for the ideals of free speech; the hacker position was generally that of what we would now understand as “radical” free speech, which is to say the kind of “shout ‘fire’ in a crowded movie theater” radical, but more specifically the kind that tolerates hate speech. The popular refrain went, “I disapprove of what you say, but I will defend to the death your right to say it”. Many hackers hold this as a virtue to this day. I once held this as a virtue for myself.&lt;/p&gt;&lt;p&gt;However, this was a kind of free speech which was unconsciously contingent on being used for speech with which the listener was comfortable. The hacker community at this time was largely homogeneous, and as such most of the speech we were exposed to was of the comfortable sort. As the world evolved around us, and more people found their voice, this homogeneity began to break down. Critics of radical free speech, victims of hate speech, and marginalized people of all kinds began to appear in hacker communities. The things they had to say were not comfortable.&lt;/p&gt;&lt;p&gt;The free speech absolutists among the old guard, faced with this discomfort, developed a tendency to defend hate speech and demean speech that challenged them. They were not the target of the hate, so it did not make them personally uncomfortable, and defending it would maintain the pretense of defending free speech, of stalwartly holding the line on a treasured part of their personal hacker ethic. Speech which challenged their preconceptions and challenged their power structures was not so easily acceptable. The pretense is dropped and they lash out in anger, calling for the speakers to be excluded from our communities.&lt;/p&gt;&lt;p&gt;Some of the once-forbidden topics are becoming less so. There are carefully chalked-out spaces where we can talk about them, provided they are not too challenging, such as LGBTQ identities or the struggles of women in our spaces. Such discussions are subject to careful management by our leaders and moderators, to the extent necessary to preserve power structures. Those who speak on these topics are permitted to do so relatively free of retaliation provided that they speak from a perspective of humility, a voice that “knows its place”. Any speech which suggests that the listener may find themselves subject to a non-majority-conforming person in a position of power, or even that of a peer, will have crossed the line; one must speak as a victim seeking the pity and grace of your superiors to be permitted space to air your grievances.&lt;/p&gt;&lt;p&gt;Similarly, space is made for opposition to progressive speech, again moderated only insofar as it is necessary to maintain power structures. Some kinds of overt hate speech may rouse a response from our leaders, but those who employ a more subtle approach are permitted their voice. Thus, both progressive speech and hate speech are permitted within a carefully regulated framework of power preservation.&lt;/p&gt;&lt;p&gt;Some topics, however, remain strictly forbidden.&lt;/p&gt;&lt;p&gt;Our community has persistent and pervasive problems of a particular sort which we are not allowed to talk about: sexual harassment and assault. Men who assault, harass, and even rape women in our spaces, are protected. A culture of silence is enforced, and those who call out rape, sexual assault, or harassment, those who criticise they who enable and protect these behaviors, are punished, swiftly and aggressively.&lt;/p&gt;&lt;p&gt;Men are terrified of these kinds of allegations. It seems like a life sentence: social ostracization, limited work opportunities, ruined relationships. We may have events in our past that weigh on our conscience; was she too drunk, did she clearly consent, did she regret it in the morning? Some of us have events in our past that we try not to think about, because if we think too hard, we might realize that we crossed the line. This fills men with guilt and uncertainty, but also fear. We know the consequences if our doubts became known.&lt;/p&gt;&lt;p&gt;So we lash out in this fear. We close ranks. We demand the most stringent standards of evidence to prove anything, evidence that we know is not likely to be there. We refuse to believe that our friends were not the men we thought they were, or to confront that we might not be ourselves. We demand due process under the law, we say they should have gone to the police, that they can’t make accusations of such gravity without hard proof. Think of the alleged perpetrator; we can’t ruin their lives over frivolous accusations.&lt;/p&gt;&lt;p&gt;For victims, the only recourse permitted by society is to suffer in silence. Should they speak, victims are subject to similar persecutions: they are ostracized, struggle to work, and lose their relationships. They have to manage the consequences of a traumatic experience with support resources which are absent or inadequate. Their trauma is disbelieved, their speech is punished, and their assailants walk free among us as equals while they are subject to retaliatory harassment or worse.&lt;/p&gt;&lt;p&gt;Victims have no recourse which will satisfy men. Reporting a crime is traumatic, especially one of this nature. I have heard many stories of disbelief from the authorities, disbelief in the face of overwhelming evidence. They were told it was their fault. They were told they should have been in a different place, or wearing something else, or should have simply been a different person. It’s their fault, not the aggressor’s. It’s about what they, the victim, should have done differently, never mind what the perpetrator should have done differently. It’s estimated that less than 1% of rapes end with the rapist in jail&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; – the remainder go unreported, unprosecuted or fail after years of traumatic legal proceedings for the victims. The legal system does not provide justice: it exacerbates harm. A hacker will demand this process is completed before they will seek justice, or allow justice to be sought. Until then, we will demand silence, and retaliate if our demands are not met.&lt;/p&gt;&lt;p&gt;The strict standards of evidence required by the justice system are there because of the state monopoly on violence: a guilty verdict in a crime will lead to the imprisonment of the accused. We have no such recourse available in private, accordingly there is no need to hold ourselves to such standards. Our job is not to punish the accused, but rather to keep our communities safe. We can establish the need to take action to whatever standard &lt;em&gt;we&lt;/em&gt; believe is sufficient, and by setting these standards as strict as the courts we will fail to resolve over 99% of the situations with which we are faced – a standard which is clearly not sufficient to address the problem. I’m behind you if you want to improve the justice system in this regard, but not if you set this as a blocker to seeking any justice at all. What kind of hacker puts their faith in authority?&lt;/p&gt;&lt;p&gt;I find the state of affairs detestable. The hypocrisy of the free speech absolutist who demands censorship of challenging topics. The fact that the famous hacker curiosity can suddenly dry up if satisfying it would question our biases and preconceptions. The complicity of our moderators in censoring progressive voices in the defense of decorum and the status quo. The duplicitous characterization of “polite” hate speech as acceptable in our communities. Our failure to acknowledge our own shortcomings, our fear of seeing the “other” in a position of power, and the socially enforced ignorance of the “other” that naturally leads to failing to curtail discrimination and harassment in our communities. The ridiculously high standard of evidence we require from victims, who simply ask for our &lt;em&gt;belief&lt;/em&gt; at a minimum, before we’ll consider doing anything about their grievance, if we could even be convinced in the first place.&lt;/p&gt;&lt;p&gt;Meanwhile, the problems that these forbidden topics seek to discuss are present in our community. That includes the “polite” problems, such as the conspicuous lack of diversity in our positions of power, which may be discussed and commiserated only until someone suggests doing something about it; and also the impolite problems up to and including the protection of the perpetrators of sexual harassment, sexual assault, and, yes, rape.&lt;/p&gt;&lt;p&gt;Most hackers live under the comfortable belief that it “can’t happen here”, but it can and it does. I attended a hacker event this year – HiP Berlin – where I discovered that some of the organizers had cooperated to make it possible for multiple known rapists to participate, working together to find a way to circumvent the event’s code of conduct – a document that they were tasked with enforcing. One of the victims was in attendance, believing the event to be safe. At every hacker event I have attended in recent memory, I have personally witnessed or heard stories of deeply problematic behavior and protection for its perpetrators from the leadership.&lt;/p&gt;&lt;p&gt;Our community has problems, important problems, that every hacker should care about, and we need the bravery and humility to face them, not the cowardice to retaliate against those who speak up. Talk to, listen to, and believe your peers and their stories. Stand up for what’s right, and speak out when you see something that isn’t. Demand that your leaders and moderators do the right thing. Make a platform where people can safely speak about what our community needs to do right by them, and have the courage to listen to them and confront yourself.&lt;/p&gt;&lt;p&gt;You need to be someone who will &lt;em&gt;do something about it&lt;/em&gt;.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: Case in point: this post was quietly removed by Hacker News moderators within 40 minutes of its submission.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-forbidden-topics/</link>
        
        <pubDate>Fri, 29 Sep 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-forbidden-topics/</guid>
      </item>
    
      <item>
        
        
          <title>Hyprland is a toxic community</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://hyprland.org/&quot; target=&quot;_blank&quot;&gt;Hyprland&lt;/a&gt; is an open source Wayland compositor based on &lt;a href=&quot;https://gitlab.freedesktop.org/wlroots/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;, a project I started back in 2017 to make it easier to build good Wayland compositors. It’s a project which is loved by its users for its emphasis on customization and “eye candy” – beautiful graphics and animations, each configuration tailored to the unique look and feel imagined by the user who creates it. It’s a very exciting project!&lt;/p&gt;&lt;p&gt;Unfortunately, the effect is spoilt by an incredibly toxic and hateful community. I cannot recommend Hyprland to anyone who is not prepared to steer well clear of its community spaces. Imagine a high school boys’ locker room come to life on Discord and GitHub and you’ll get an idea of what it’s like.&lt;/p&gt;&lt;p&gt;I became aware of the issues with Hyprland’s community after details of numerous hateful incidents on their Discord came to my attention by way of  the grapevine. Most of them stem from the community’s tolerance of hate: community members are allowed to express hateful views with impunity, up to and including astonishing views such as endorsements of eugenics and calls for hate-motivated violence. Such comments are treated as another act in the one big inside joke that is the Hyprland community – the community prefers not to take itself “too seriously”. Hate is moderated only if it is “disruptive” (e.g. presents as spam), but hate presented with a veneer of decorum (or sarcasm) is tolerated, and when challenged, it’s laughed off as a joke.&lt;/p&gt;&lt;p&gt;In one particular incident, the moderators of the Discord server engaged in a harassment campaign against a transgender user, including using their moderator privileges to edit the pronouns in their username from “they/she” to “who/cares”. These roles should be held by trusted community leaders, and it’s from their behavior that the community’s culture and norms stem – they set an example for the community and define what behaviors are acceptable or expected. The problem comes from the top down.&lt;/p&gt;&lt;p&gt;Someone recently pitched a code of conduct – something that this project sorely needs – in a &lt;a href=&quot;https://web.archive.org/web/20230917015135/https://github.com/hyprwm/Hyprland/issues/3209&quot; target=&quot;_blank&quot;&gt;GitHub issue&lt;/a&gt;. This thread does not have much overt hate, but it does clearly show how callous and just plain &lt;em&gt;mean&lt;/em&gt; the community is, including its leadership (Vaxerski is the original author of Hyprland). Everything is a joke and anyone who wants to be “serious” about anything is mercilessly bullied and made fun of. Quoting this discussion:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I think [a Code of Conduct] is pretty discriminatory towards people that prefer a close, hostile, homogeneous, exclusive, and unhealthy community.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;First of all, why would I pledge to uphold any values? Seems like just inconveniencing myself. […] If I’d want to moderate, I’d spend 90% of the time reading kids arguing about bullshit instead of coding.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;If you don’t know how to behave without a wall of text explaining how to behave online then you shouldn’t be online.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I am not someone who believes all projects need a code of conduct, &lt;em&gt;if&lt;/em&gt; there exists a reasonable standard of conduct in its absence – and that means having a community that does not bully and harass others for expressing differing points of view, let alone for simply having a marginalized identity.&lt;/p&gt;&lt;p&gt;I would have preferred to address these matters in private, so I reached out to Vaxry in February. He responded with a lack of critical awareness over how toxicity presents in his community. However, following my email, he put out a poll for the Discord community to see if the community members experienced harassment in the community – apparently 40% of respondents reported such experiences. Vaxry et al implemented new moderation policies as a result. But these changes did not seem to work: the problems are still present, and the community is still a toxic place that facilitates bullying and hate, including from the community leaders.&lt;/p&gt;&lt;p&gt;Following my email conversation with Vaxry, he appeared on &lt;a href=&quot;https://invidious.mnus.de/watch?v=nskemNa_Kn4&quot; target=&quot;_blank&quot;&gt;a podcast&lt;/a&gt; to discuss toxicity in the Hyprland community. This quote from the interview clearly illustrates the attitude of the leadership:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[A trans person] joined the Discord server and made a big deal out of their pronouns [..] because they put their pronouns in their nickname and made a big deal out of them because people were referring to them as “he” [misgendering them], which, on the Internet, let’s be real, is the default. And so, one of the moderators changed the pronouns in their nickname to “who/cares”. […] Let’s be real, this isn’t like, calling someone the N-word or something.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Later he describes a more moderated community (the /r/unixporn discord server) as having an environment in which everyone is going to “lick your butthole just to be nice”. He compared himself to &lt;a href=&quot;https://en.wikipedia.org/wiki/Terry_A._Davis&quot; target=&quot;_blank&quot;&gt;Terry Davis&lt;/a&gt;, the late operating system developer whose struggles with mental illness were broadcast for the world to see, citing a video in which he answers a phone call and refers to the person on the phone by the N-word “ten times” – Vaxry compares this to his approach to answering “stupid questions”.&lt;/p&gt;&lt;p&gt;It really disappoints me to see such an exciting project brought low by a horribly mismanaged community of hate and bullying. Part of what makes open source software great is that it’s great for everyone. It’s unfortunate that someone can discover this cool project, install it and play with it and get excited about it, then join the community to find themselves at the wrong end of this behavior. No one deserves that.&lt;/p&gt;&lt;p&gt;I empathise with Vaxry. I remember being young, smart, productive… and mean. I did some cool stuff, but I deeply regret the way I treated people. It wasn’t really my fault – I was a product of my environment – but it was my responsibility. Today, I’m proud to have built many welcoming communities, where people are rewarded for their involvement, rather than coming away from their experience hurt. What motivates us to build and give away free software if not bringing joy to ourselves and others? Can we be proud of a community which brings more suffering into the world?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;My advice to the leadership&lt;/strong&gt; begins with taking a serious look in the mirror. This project needs a “come to Jesus” moment. Ask yourself what kind of community you can be proud of – can you be proud of a community that people walk away from feeling dejected and hurt? Yours is not a community that brings people joy. What are you going to do about it?&lt;/p&gt;&lt;p&gt;A good start will be to consider the code of conduct proposal seriously, but a change of attitude is also required. &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;My inbox&lt;/a&gt; is open to any of the leaders in this project (or any other project facing similar problems) if you want to talk. I’m happy to chat with you in good faith and help you understand what’s needed and why it’s important.&lt;/p&gt;&lt;p&gt;To members of the &lt;strong&gt;Hyprland community&lt;/strong&gt;, I want each of you to personally step up to make the community better. If you see hate and bullying, don’t stay silent. This is a community which proclaims to value radical free speech: test it by using your speech to argue against hate. Participate in the community as you think it should be, not as it necessarily is, and change will follow. If you are sensitive to hate, or a member of a marginalized group, however, I would just advise steering clear of Hyprland until the community improves.&lt;/p&gt;&lt;p&gt;If the leadership fails to account for these problems, it will be up to the community to take their activity elsewhere. You could set up adjacent communities which are less toxic, or fork the software, or simply choose to use something else.&lt;/p&gt;&lt;p&gt;To the &lt;strong&gt;victims of harassment&lt;/strong&gt;, I offer my sincere condolences. I know how hard it is to be the subject of this kind of bullying. You don’t deserve to be treated like this. There are many places in the free software community where you are welcome and celebrated – Hyprland is &lt;em&gt;not&lt;/em&gt; the norm. If you need support, I’m &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;always available&lt;/a&gt; to listen to your struggles.&lt;/p&gt;&lt;p&gt;To everyone else: please share this post throughout the Hyprland community and adjacent communities. This is a serious problem and it’s not going to change unless its clearly brought to light. The Hyprland maintainers need to be made aware that the broader open source community does not appreciate this kind of behavior.&lt;/p&gt;&lt;p&gt;I sincerely hope that this project improves its community. A serious attitude shift is needed from the top-down, and I hope for the sake of Vaxry, the other leaders, and the community as a whole, that such change comes sooner rather than later. When Vaxry is older and wiser, I want him to look back on the project and community that he’s built with pride and joy, not with regret and shame.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Vaxry has published &lt;a href=&quot;https://blog.vaxry.net/articles/2023-hyprlandsCommunity&quot; target=&quot;_blank&quot;&gt;a response&lt;/a&gt; to this post.&lt;/p&gt;&lt;p&gt;I was also privately provided some of the enusing discussion from the Hyprland Discord. Consider that this lacks context and apply your grain of salt accordingly.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/28580a3a.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a Discord channel with the initial reaction to this post. A user called “slave labor” responds with “no way”, “the computer reddit woke up”&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/0d233e9f.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a Discord channel with Vaxry’s initial reaction to this post. “Really, right as I wanted to take a day off because of health reasons I have to reply to this?”. Another user responds “wow this is quite… shallow”, “almost as if it recycles very limited context to get more clicks”&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I apologise to Vaxry for interrupting their rest, and wish them a speedy recovery.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/53c4bc32.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a Discord channel. Some notable quotes include “LGBTQ is fucking trash anyways” (someone else responds “fuck off” to this) and “for reclaiming polymc from the leftoids”. The discussion as a whole lacks any sembelance of professionalism.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://paste.sr.ht/~sircmpwn/093af570609ec87e987af6cc69c59e9624c2b280&quot; target=&quot;_blank&quot;&gt;Here&lt;/a&gt; is a plain text log which includes some additional discussion.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Hyprland-toxicity/</link>
        
        <pubDate>Sun, 17 Sep 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Hyprland-toxicity/</guid>
      </item>
    
      <item>
        
        
          <title>AI crap</title>
          <description>
            &lt;p&gt;There is a machine learning bubble, but the technology is here to stay. Once the bubble pops, the world &lt;em&gt;will&lt;/em&gt; be changed by machine learning. But it will probably be crappier, not better.&lt;/p&gt;&lt;p&gt;Contrary to the AI doomer’s expectations, the world isn’t going to go down in flames any faster thanks to AI. Contemporary advances in machine learning aren’t really getting us any closer to AGI, and as Randall Monroe pointed out back in 2018:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/robot_future_2x.png&quot; alt=&quot;A panel from the webcomic &apos;xkcd&apos; showing a timeline from now into the distant future, dividing the timeline into the periods between &apos;AI becomes advanced enough to control unstoppable swarms of robots&apos; and &apos;AI becomes self-aware and rebels against human control&apos;. The period from self-awareness to the indefinite future is labelled &apos;the part lots of people seem to worry about&apos;; Randall is instead worried about the part between these two epochs.&quot;&gt;&lt;/p&gt;&lt;p&gt;What will happen to AI is boring old capitalism. Its staying power will come in the form of replacing competent, expensive humans with crappy, cheap robots. LLMs are a pretty good advance over Markov chains, and stable diffusion can generate images which are only somewhat uncanny with sufficient manipulation of the prompt. Mediocre programmers will use GitHub Copilot to write trivial code and boilerplate for them (trivial code is tautologically uninteresting), and ML will probably remain useful for writing cover letters for you. Self-driving cars might show up Any Day Now™, which is going to be great for sci-fi enthusiasts and technocrats, but much worse in every respect than, say, &lt;a href=&quot;https://www.youtube.com/watch?v=0dKrUE_O0VE&quot; target=&quot;_blank&quot;&gt;building more trains&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The biggest lasting changes from machine learning will be more like the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A reduction in the labor force for skilled creative work&lt;/li&gt;&lt;li&gt;The complete elimination of humans in customer-support roles&lt;/li&gt;&lt;li&gt;More convincing spam and phishing content, more scalable scams&lt;/li&gt;&lt;li&gt;SEO hacking content farms dominating search results&lt;/li&gt;&lt;li&gt;Book farms (both eBooks and paper) flooding the market&lt;/li&gt;&lt;li&gt;AI-generated content overwhelming social media&lt;/li&gt;&lt;li&gt;Widespread propaganda and astroturfing, both in politics and advertising&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;AI companies will continue to generate waste and CO&lt;sub&gt;2&lt;/sub&gt; emissions at a huge scale as they aggressively scrape all internet content they can find, externalizing costs onto the world’s digital infrastructure, and feed their hoard into GPU farms to generate their models. They might keep humans in the loop to help with tagging content, seeking out the cheapest markets with the weakest labor laws to build human sweatshops to feed the AI data monster.&lt;/p&gt;&lt;p&gt;You will never trust another product review. You will never speak to a human being at your ISP again. Vapid, pithy media will fill the digital world around you. Technology built for engagement farms – those AI-edited videos with the grating machine voice you’ve seen on your feeds lately – will be white-labeled and used to push products and ideologies at a massive scale with a minimum cost from social media accounts which are populated with AI content, cultivate an audience, and sold in bulk and in good standing with the Algorithm.&lt;/p&gt;&lt;p&gt;All of these things are already happening and will continue to get worse. The future of media is a soulless, vapid regurgitation of all media that came before the AI epoch, and the fate of all new creative media is to be subsumed into the roiling pile of math.&lt;/p&gt;&lt;p&gt;This will be incredibly profitable for the AI barons, and to secure their investment they are deploying an immense, expensive, world-wide propaganda campaign. To the public, the present-day and potential future capabilities of the technology are played up in breathless promises of ridiculous possibility. In closed-room meetings, much more realistic promises are made of cutting payroll budgets in half.&lt;/p&gt;&lt;p&gt;The propaganda also leans into the mystical sci-fi AI canon: the threat of smart computers with world-ending power, the forbidden allure of a new Manhattan Project and all of its consequences, the long-prophesied singularity. The technology is nowhere near this level, a fact well-known by experts and the barons themselves, but the illusion is maintained in the interests of lobbying lawmakers to help the barons erect a moat around their new industry.&lt;/p&gt;&lt;p&gt;Of course, AI does present a threat of violence, but as Randall points out, it’s not from the AI itself, but rather from the people that employ it. The US military is testing out AI-controlled drones, which aren’t going to be self-aware but will scale up human errors (or human malice) until innocent people are killed. AI tools are already being used to set bail and parole conditions – it can put you in jail or keep you there. Police are using AI for facial recognition and “predictive policing”. Of course, all of these models end up discriminating against minorities, depriving them of liberty and often getting them killed.&lt;/p&gt;&lt;p&gt;AI is defined by aggressive capitalism. The hype bubble has been engineered by investors and capitalists dumping money into it, and the returns they expect on that investment are going to come out of your pocket. The singularity is not coming, but the most realistic promises of AI are going to make the world worse. The AI revolution is here, and I don’t really like it.&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;Flame bait&lt;/summary&gt;
I had much more inflammatory article drafted for this topic under the title
&quot;ChatGPT is the new techno-atheist&apos;s substitute for God&quot;. It makes some fairly
pointed comparisons between the cryptocurrency cult and the machine learning
cult and the religious, unshakeable, and largely ignorant faith in both
technologies as the harbingers of progress. It was fun to write, but this is
probably the better article.

I found this Hacker News comment and quoted it in the original draft: &quot;It&apos;s
probably worth talking to GPT4 before seeking professional help [to deal with
depression].&quot;

In case you need to hear it: [do not][suicide] (TW: suicide) seek out OpenAI&apos;s
services to help with your depression. Finding and setting up an appointment
with a therapist can be difficult for a lot of people -- it&apos;s okay for it to
feel hard. Talk to your friends and ask them to help you find the right care for
your needs.
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/AI-crap/</link>
        
        <pubDate>Tue, 29 Aug 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/AI-crap/</guid>
      </item>
    
      <item>
        
        
          <title>Hello from Ares!</title>
          <description>
            &lt;p&gt;I am pleased to be writing today’s blog post from a laptop running &lt;a href=&quot;https://ares-os.org&quot; target=&quot;_blank&quot;&gt;Ares OS&lt;/a&gt;. I am writing into an ed(1) session, on a file on an ext4 filesystem on its hard drive. That’s pretty cool! It seems that a lot of interesting stuff has happened since I gave that talk on Helios at &lt;a href=&quot;https://spacepub.space/w/wpKXfhqqr7FajEAf4B2Vc2&quot; target=&quot;_blank&quot;&gt;FOSDEM&lt;/a&gt; in February.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/68a47ef3.jpg&quot;&gt;
&lt;figcaption&gt;A picture of my ThinkPad while I was editing this blog post&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;The talk I gave at FOSDEM was no doubt impressive, but it was a bit of a party trick. The system was running on a Raspberry Pi with one process which included both the slide deck as a series of raster images baked into the ELF file, as well as the GPU driver and drawing code necessary to display them, all in one package. This was quite necessary, as it turns out, given that the very idea of “processes” was absent from the system at this stage.&lt;/p&gt;&lt;p&gt;Much has changed since that talk. The system I am writing to you from has support for processes indeed, complete with fork and exec and auxiliary vectors and threads and so on. If I run “ps” I get the following output:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;mercury % ps
1 /sbin/usrinit dexec /sbin/drv/ext4 block0 childfs 0 fs 0
2 /etc/driver.d/00-pcibus
3 /etc/pci.d/class/01/06/ahci
4 /etc/driver.d/00-ps2kb
5 /etc/driver.d/99-serial
6 /etc/driver.d/99-vgacons
7 /sbin/drv/ext4 block0
15 ed blog.md
16 ps
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each of these processes is running in userspace, and some of them are drivers. A number of drivers now exist for the system, including among the ones you see here a general-purpose PCI driver, AHCI (SATA), PS/2 keyboard, PC serial, and a VGA console, not to mention the ext4 driver, based on lwext4 (the first driver not written in Hare, actually). Not shown here are additional drivers for the CMOS real-time clock (so Ares knows what time it is, thanks to Stacy Harper), a virtio9pfs driver (thanks also to Tom Leb for the initial work here), and a few more besides.&lt;/p&gt;&lt;p&gt;As of this week, a small number of software ports exist. The ext4 driver is based on lwext4, as I said earlier, which might be considered a port, though it is designed to be portable. The &lt;a href=&quot;https://git.sr.ht/~sircmpwn/rc&quot; target=&quot;_blank&quot;&gt;rc&lt;/a&gt; shell I have been working on lately has also been ported, albeit with many features disabled, to Mercury. And, of course, I did say I was writing this blog post with ed(1) – I have ported Michael Forney’s &lt;a href=&quot;http://git.suckless.org/sbase/file/ed.c.html&quot; target=&quot;_blank&quot;&gt;ed implementation&lt;/a&gt; from sbase, with &lt;a href=&quot;https://git.sr.ht/~sircmpwn/sbase/commit/ee0336bc3b6f55839785427d6184e6f897055e31&quot; target=&quot;_blank&quot;&gt;relatively few&lt;/a&gt; features disabled as a matter of fact (the “!” command and signals were removed).&lt;/p&gt;&lt;p&gt;This ed port, and lwext4, are based on our C library, designed with drivers and normal userspace programs in mind, and derived largely from musl libc. This is coming along rather well – a few features (signals again come to mind) are not going to be implemented, but it’s been relatively straightforward to get a large amount of the POSIX/C11 API surface area covered on Ares, and I was pleasantly surprised at how easy it was to port ed(1).&lt;/p&gt;&lt;p&gt;There’s still quite a lot to be done. In the near term, I expect to see the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A virtual filesystem&lt;/li&gt;&lt;li&gt;Pipes and more shell features enabled, such as redirects&lt;/li&gt;&lt;li&gt;More filesystem support (mkdir et al)&lt;/li&gt;&lt;li&gt;A framebuffer console&lt;/li&gt;&lt;li&gt;EFI support on x86_64&lt;/li&gt;&lt;li&gt;MBR and GPT partitions&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is more of the basics. As these basics unblock other tasks, a few of the more ambitious projects we might look forward to include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Networking support (at least ICMP)&lt;/li&gt;&lt;li&gt;Audio support&lt;/li&gt;&lt;li&gt;ACPI support&lt;/li&gt;&lt;li&gt;Basic USB support&lt;/li&gt;&lt;li&gt;A service manager (&lt;em&gt;not&lt;/em&gt; systemd…)&lt;/li&gt;&lt;li&gt;An installer, perhaps a package manager&lt;/li&gt;&lt;li&gt;Self-hosting builds&lt;/li&gt;&lt;li&gt;Dare I say Wayland?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I should also probably do something about that whining fan I’m hearing in the background right now. Of course, I will also have to do a fresh DOOM port once the framebuffer situation is improved. There’s also still plenty of kernel work to be done and odds and ends all over the project, but it’s in pretty good shape and I’m having a blast working on it. I think that by now I have answered the original question, “can an operating system be written in Hare”, with a resounding “yes”. Now I’m just having fun with it. Stay tuned!&lt;/p&gt;&lt;p&gt;Now I just have to shut this laptop off. There’s no poweroff command yet, so I suppose I’ll just hold down the power button until it stops making noise.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Hello-from-Ares/</link>
        
        <pubDate>Wed, 09 Aug 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Hello-from-Ares/</guid>
      </item>
    
      <item>
        
        
          <title>The rc shell and its excellent handling of whitespace</title>
          <description>
            &lt;p&gt;&lt;em&gt;This blog post is a response to Mark Dominus’ “&lt;a href=&quot;https://blog.plover.com/Unix/whitespace.html&quot; target=&quot;_blank&quot;&gt;The shell and its crappy handling of whitespace&lt;/a&gt;”&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I’ve been working on a shell for Unix-like systems called &lt;a href=&quot;https://git.sr.ht/~sircmpwn/rc&quot; target=&quot;_blank&quot;&gt;rc&lt;/a&gt;, which draws heavily from the Plan 9 shell &lt;a href=&quot;http://man.9front.org/1/rc&quot; target=&quot;_blank&quot;&gt;of the same name&lt;/a&gt;. When I saw Mark’s post about the perils of whitespace in POSIX shells (or derived shells, like bash), I thought it prudent to see if any of the problems he outlines are present in the shell I’m working on myself. Good news: they aren’t!&lt;/p&gt;&lt;p&gt;Let’s go over each of his examples. First he provides the following example:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;for i in *.jpg; do
	cp $i /tmp
done
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This breaks if there are spaces in the filenames. Not so with rc:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;% cat test.rc
for (i in *.jpg) {
	cp $i subdir
}
% ls
a.jpg   b.jpg  &amp;apos;bite me.jpg&amp;apos;   c.jpg   subdir   test.rc
% rc ./test.rc 
% ls subdir/
a.jpg   b.jpg  &amp;apos;bite me.jpg&amp;apos;   c.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;He gives a similar example for a script that renames jpeg to jpg:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;for i in *.jpeg; do
  mv $i $(suf $i).jpg
done
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This breaks for similar reasons, but works fine in rc:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;% cat test.rc  
fn suf(fname) {
	echo $fname | sed -e &amp;apos;s/\..*//&amp;apos;
}

for (i in *.jpeg) {
	mv $i `{suf $i}.jpg
}
% ls 
a.jpeg   b.jpeg  &amp;apos;bite me.jpeg&amp;apos;   c.jpeg   test.rc
% rc ./test.rc  
% ls 
a.jpg   b.jpg  &amp;apos;bite me.jpg&amp;apos;   c.jpg   test.rc
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are other shells, such as fish or zsh, which also have answers to these problems which don’t necessarily call for generous quoting like other shells often do. rc is much simpler than these shells. At the moment it clocks in at just over 3,000 lines of code, compared to fish at ~45,000 and zsh at ~144,000. Admittedly, it’s not done yet, but I would be surprised to see it grow beyond 5,000 lines for version 1.0.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The key to rc’s design success in this area is the introduction of a second primitive. The Bourne shell and its derivatives traditionally work with only one primitive: strings. But command lines are made of &lt;em&gt;lists&lt;/em&gt; of strings, and so a language which embodies the primitives of the command line ought to also be able to represent those as a first-class feature. In traditional shells a list of strings is denoted inline with the use of spaces within those strings, which raises obvious problems when the members themselves contain spaces; see Mark’s post detailing the errors which ensue. rc adds lists of strings as a formal primitive alongside strings.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;% args=(ls --color /) 
% echo $args(1) 
ls
% echo $args(2) 
--color
% echo $#args 
3
% $args 
bin   dev  home  lost+found  mnt  proc  run   srv      swap  tmp  var
boot  etc  lib   media       opt  root  sbin  storage  sys   usr
% args=(&amp;quot;foo bar&amp;quot; baz) 
% touch $args 
% ls 
 baz  &amp;apos;foo bar&amp;apos;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Much better, right? One simple change eliminates the need for quoting virtually everywhere. Strings can contain spaces and nothing melts down.&lt;/p&gt;&lt;p&gt;Let me run down the remaining examples from Mark’s post and demonstrate their non-importance in rc. First, regarding $*, it just does what you expect.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;% cat yell.rc
#!/bin/rc
shift
echo I am about to run $* now!!!
exec $*
% ls *.jpg
&amp;apos;bite me.jpg&amp;apos;
% ./yell.rc ls *.jpg
I am about to run ls bite me.jpg now!!!
&amp;apos;bite me.jpg&amp;apos;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note also that there is no need to quote the arguments to “echo” here. Also note the use of shift; $* includes $0 in rc.&lt;/p&gt;&lt;p&gt;Finally, let’s rewrite Mark’s “lastdl” program in rc and show how it works fine in rc’s interactive mode.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#!/bin/rc
cd $HOME/downloads
echo $HOME/downloads/`{ls -t | head -n1}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Its use at the command line works just fine without quotes.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;% file `{lastdl} 
/home/sircmpwn/downloads/test image.jpg: JPEG image data, JFIF standard 1.01,
aspect ratio, density 1x1, segment length 16, baseline, precision 8,
5000x2813, components 3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just for fun, here’s another version of this rc script that renames files with spaces to without, like the last example in Mark’s post:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#!/bin/rc
cd $HOME/downloads
last=`{ls -t | head -n1}
if (~ $last &amp;apos;* *&amp;apos;) {
	newname=`{echo $last | tr &amp;apos; \t&amp;apos; &amp;apos;_&amp;apos;}
	mv $last $HOME/downloads/$newname
	last=$newname
}
echo $HOME/downloads/$last
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The only quotes to be found are those which escape the wildcard match testing for a space in the string.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; Not bad, right? Like Plan 9’s rc, my shell imagines a new set of primitives for shells, then starts from the ground up and builds a shell which works better in most respects while still being very simple. Most of the problems that have long plagued us with respect to sh, bash, etc, are solved in a simple package with rc, alongside a nice interactive mode reminiscent of the best features of fish.&lt;/p&gt;&lt;p&gt;rc is a somewhat complete shell today, but there is a bit more work to be done before it’s ready for 1.0, most pressingly with respect to signal handling and job control, alongside a small bit of polish and easier features to implement (such as subshells, IFS, etc). Some features which are likely to be omitted, at least for 1.0, include logical comparisons and arithmetic expansion (for which /bin/test and /bin/dc are recommended respectively). Of course, rc is destined to become the primary shell of the &lt;a href=&quot;https://ares-os.org&quot; target=&quot;_blank&quot;&gt;Ares operating system&lt;/a&gt; project that I’ve been working on, but I have designed it to work on Unix as well.&lt;/p&gt;&lt;p&gt;Check it out!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-rc-shell-and-whitespace/</link>
        
        <pubDate>Mon, 31 Jul 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-rc-shell-and-whitespace/</guid>
      </item>
    
      <item>
        
        
          <title>Alpine Linux does not make the news</title>
          <description>
            &lt;p&gt;My Linux distribution of choice for several years has been &lt;a href=&quot;https://alpinelinux.org/&quot; target=&quot;_blank&quot;&gt;Alpine Linux&lt;/a&gt;. It’s a small, efficient distribution which ships a number of tools I appreciate for their simplicity, such as musl libc. It has a very nice package manager, apk, which is fast and maintainable. The development community is professional and focuses on diligent maintenance of the distribution and little else. Over the years I have used it, very little of note has happened.&lt;/p&gt;&lt;p&gt;I run Alpine in every context; on my workstation and my laptops but also on production servers, on bare-metal and in virtual machines, on my RISC-V and ARM development boards, at times on my phones, and in many other contexts besides. It has been a boring experience. The system is simply reliable, and the upgrades go over without issue every other quarter,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; accompanied by high-quality release notes. I’m pleased to maintain several dozen packages in the repositories, and the community is organized such that it is easy for someone like me to jump in and do the work required to maintain it for my use-cases.&lt;/p&gt;&lt;p&gt;Red Hat has been in the news lately for their moves to monetize the distribution, moves that I won’t comment on but which have generally raised no small number of eyebrows, written several headlines, and caused intense flamewars throughout the internet. I don’t run RHEL or CentOS anywhere, in production or otherwise, so I just looked curiously on as all of this took place without calling for any particular action on my part. Generally speaking, Alpine does not make the news.&lt;/p&gt;&lt;p&gt;And so it has been for years, as various controversies come about and die off, be it with Red Hat, Ubuntu, Debian, or anything else, I simply keep running “apk upgrade” every now and then and life goes on uninterrupted. I have high-quality, up-to-date software on a stable system and suffer from no fuss whatsoever.&lt;/p&gt;&lt;p&gt;The Alpine community is a grassroots set of stakeholders who diligently concern themselves with the business of maintaining a good Linux distribution. There is little in the way of centralized governance;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; for the most part the distribution is just quietly maintained by the people who use it for the purpose of ensuring its applicability to their use-cases.&lt;/p&gt;&lt;p&gt;So, Alpine does not make the news. There are no commercial entities which are trying to monetize it, at least no more than the loosely organized coalition of commercial entities like SourceHut that depend on Alpine and do their part to keep it in good working order, alongside various users who have no commercial purpose for the system. The community is largely in unanimous agreement about the fundamental purpose of Alpine and the work of the community is focused on maintaining the project such that this purpose is upheld.&lt;/p&gt;&lt;p&gt;This is a good trait for a Linux distribution to have.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Alpine-does-not-make-news/</link>
        
        <pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Alpine-does-not-make-news/</guid>
      </item>
    
      <item>
        
        
          <title>Seriously, don&apos;t sign a CLA</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://about.sourcegraph.com/&quot; target=&quot;_blank&quot;&gt;SourceGraph&lt;/a&gt; is making their product closed source, abandoning the Apache 2.0 license it was originally distributed under, so once again we convene in the ritual condemnation we offer to commercial products that piss in the pool of open source. Invoking Bryan Cantrill once more:&lt;/p&gt;&lt;iframe
  width=&quot;560&quot;
  height=&quot;315&quot;
  src=&quot;https://www.youtube-nocookie.com/embed/-zRN7XLCRhc?start=2483&quot;
  frameborder=&quot;0&quot;
  allow=&quot;accelerometer; autoplay; gyroscope; picture-in-picture&quot;
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;
&lt;a
  style=&quot;display: block; text-align: center&quot;
  href=&quot;https://youtu.be/-zRN7XLCRhc?t=2483&quot;
&gt;&lt;small&gt;Bryan Cantrill on OpenSolaris &amp;mdash; YouTube&lt;/small&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;A contributor license agreement, or CLA, usually (but not always) includes an important clause: a copyright assignment. These agreements are provided by upstream maintainers to contributors to open source software projects, and they demand a signature before the contributor’s work is incorporated into the upstream project. The copyright assignment clause that is usually included serves to offer the upstream maintainers more rights over the contributor’s work than the contributor was offered by upstream, generally in the form of ownership or effective ownership over the contributor’s copyright and the right to license it in any manner they choose in the future, including proprietary distributions.&lt;/p&gt;&lt;p&gt;This is a strategy employed by commercial companies with one purpose only: to place a rug under the project, so that they can pull at the first sign of a bad quarter. This strategy exists to subvert the open source social contract. These companies wish to enjoy the market appeal of open source and the free labor of their community to improve their product, but do &lt;em&gt;not&lt;/em&gt; want to secure these contributors any rights over their work.&lt;/p&gt;&lt;p&gt;This is particularly pathetic in cases like that of SourceGraph, which used a permissive Apache 2.0 license. Such licenses already allow their software to be incorporated into non-free commercial works, such is the defining nature of a permissive license, with relatively few obligations: in this case, a simple attribution will suffice. SourceGraph could have been made non-free without a CLA at all if this one obligation was met. The owners of SourceGraph find the simple task of crediting their contributors too onerous. This is disgusting.&lt;/p&gt;&lt;p&gt;SourceGraph once approached SourceHut asking about building an integration between our platforms. They wanted us to do most of the work, which is a bit tacky but reasonable under the reciprocal social contract of open source. We did not prioritize it and I’m glad that we didn’t: our work would have been made non-free.&lt;/p&gt;&lt;p&gt;Make no mistake: a CLA is a promise that a open source software project will one day become non-free. Don’t sign them.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;What are my rights as a contributor?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If you sign away your rights by agreeing to a CLA, you retain all of the rights associated with your work.&lt;/p&gt;&lt;p&gt;By default, you own the copyright over your contribution and the contribution is licensed under the same software license the original project uses, thus, your contribution is offered to the upstream project on the same terms that their contribution was offered to you. The copyright for such projects is held collectively by all contributors.&lt;/p&gt;&lt;p&gt;You also always have the right to fork an open source project and distribute your improvements on your own terms, without signing a CLA – the only power upstream holds is authority over the “canonical” distribution. If the rug is pulled from under you, you may also continue to use, and improve, versions of the software from prior to the change in license.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;How do I prevent this from happening to my project?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;A CLA is a promise that software will one day become non-free; you can also promise the opposite. Leave copyright in the collective hands of all contributors and use a copyleft license.&lt;/p&gt;&lt;p&gt;Without the written consent of all contributors, or performing their labor yourself by re-writing their contributions, you cannot change the license of a project. Skipping the CLA leaves their rights intact.&lt;/p&gt;&lt;p&gt;In the case of a permissive software license, a new license (including proprietary licenses) can be applied to the project and it can be redistributed under those terms. In this way, all future changes can be written with a new license. The analogy is similar to that of a new project with a proprietary license taking a permissively licensed project and incorporating all of the code into itself before making further changes.&lt;/p&gt;&lt;p&gt;You can prevent this as well with a copyleft license: such a license requires the original maintainers to distribute future changes to the work under a free software license. Unless they can get all copyright holders – all of the contributors – to agree to a change in license, they are obligated to distribute their improvements on the same terms.&lt;/p&gt;&lt;p&gt;Thus, the absence of a CLA combined with the use of a copyleft license serves as a strong promise about the future of the project.&lt;/p&gt;&lt;p&gt;Learn more at &lt;a href=&quot;https://writefreesoftware.org&quot; target=&quot;_blank&quot;&gt;writefreesoftware.org&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://writefreesoftware.org/learn/participate/copyright-ownership/&quot; target=&quot;_blank&quot;&gt;Managing copyright ownership&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://writefreesoftware.org/learn/participate/derived-works/&quot; target=&quot;_blank&quot;&gt;Re-using free software&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://writefreesoftware.org/learn/participate/derived-works/&quot; target=&quot;_blank&quot;&gt;What is copyleft?&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;What should I do as a business instead of a CLA?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;It is not ethical to demand copyright assignment in addition to the free labor of the open source community. However, there are some less questionable aspects of a contributor license agreement which you may uphold without any ethical qualms, notably to establish provenance.&lt;/p&gt;&lt;p&gt;Many CLAs include clauses which establish the provenance of the contribution and transfer liability to the contributor, such that the contributor agrees that their contribution is either their own work or they are authorized to use the copyright (for example, with permission from their employer). This is a reasonable thing to ask for from contributors, and manages your exposure to legal risks.&lt;/p&gt;&lt;p&gt;The best way to ask for this is to require contributions to be “signed-off” with the &lt;a href=&quot;https://drewdevault.com/blog/DCO/&quot;&gt;Developer Certificate of Origin&lt;/a&gt;.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Previously:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/11/05/Apollo-federation-2-gaslighting.html&quot; target=&quot;_blank&quot;&gt;Breaking down Apollo Federation’s anti-FOSS corporate gaslighting&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/04/12/DCO.html&quot; target=&quot;_blank&quot;&gt;The Developer Certificate of Origin is a great alternative to a CLA&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/01/20/FOSS-is-to-surrender-your-monopoly.html&quot; target=&quot;_blank&quot;&gt;Open source means surrendering your monopoly over commercial exploitation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/01/19/Elasticsearch-does-not-belong-to-Elastic.html&quot; target=&quot;_blank&quot;&gt;Elasticsearch does not belong to Elastic&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Dont-sign-a-CLA-2/</link>
        
        <pubDate>Tue, 04 Jul 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Dont-sign-a-CLA-2/</guid>
      </item>
    
      <item>
        
        
          <title>Social media and &quot;parasocial media&quot;</title>
          <description>
            &lt;p&gt;A few months ago, as Elon Musk took over Twitter and instituted polices that alienated many people, some of these people fled towards federated, free software platforms like Mastodon. Many people found a new home here, but there is a certain class of refugee who has not found it to their liking.&lt;/p&gt;&lt;p&gt;I got to chatting with one such “refugee” on Mastodon today. &lt;a href=&quot;https://inv.tux.pizza/channel/UC0intLFzLaudFG-xAvUEO-A&quot; target=&quot;_blank&quot;&gt;NotJustBikes&lt;/a&gt; is a creator I enjoy watching on &lt;del&gt;YouTube&lt;/del&gt; Invidious, who makes excellent content on urbanism and the design of cities. He’s based in my home town of Amsterdam and his videos do a great job of explaining many of the things I love about this place for general audiences. He’s working on building an audience, expanding his reach, and bringing his message to as many people as possible in the interest of bringing better infrastructure to everyone.&lt;/p&gt;&lt;p&gt;But he’s not satisfied with his move from Twitter to Mastodon, nor are some of his friends among the community of “urbanist” content creators. He yearns for an “algorithm” to efficiently distribute content to his followers, and Mastodon is not providing this for him.&lt;/p&gt;&lt;p&gt;On traditional “social media” platforms, in particular YouTube, the interactions are often not especially social. The platforms facilitate a kind of intellectual consumption moreso than conversation: conversations flow in one direction, from creator to audience, where the creator produces and the audience consumes. I think a better term for these platforms is “parasocial media”: they are optimized for creating &lt;a href=&quot;https://en.wikipedia.org/wiki/Parasocial_interaction&quot; target=&quot;_blank&quot;&gt;parasocial&lt;/a&gt; relationships moreso than social relationships.&lt;/p&gt;&lt;p&gt;The fediverse is largely optimized for people having conversations with each other, and not for producing and consuming “content”. Within this framework, a “content creator” is a person only in the same sense that a corporation is, and their conversations are unidirectional, where the other end is also not a person, but an audience. That’s not the model that the fediverse is designed around.&lt;/p&gt;&lt;p&gt;It’s entirely reasonable to want to build an audience and publish content in a parasocial manner, but that’s not what the fediverse is for. And I think that’s a good thing! There are a lot of advantages in having spaces which focus on being genuinely “social”, rather than facilitating more parasocial interactions and helping creators build an audience. This limits the fediverse’s reach, but I think that’s just fine.&lt;/p&gt;&lt;p&gt;Within this model, the fediverse’s model, it’s possible to publish things, and consume things. But you cannot effectively optimize for building the largest possible audience. You will generally be more successful if you focus on the content itself, and not its reach, and on the people you connect with at a smaller scale. Whether or not this is right for you depends on your goals.&lt;/p&gt;&lt;p&gt;I hope you enjoyed this content! Remember to like and subscribe.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Social-and-parasocial-media/</link>
        
        <pubDate>Fri, 30 Jun 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Social-and-parasocial-media/</guid>
      </item>
    
      <item>
        
        
          <title>Burnout and the quiet failures of the hacker community</title>
          <description>
            &lt;p&gt;This has been a very challenging year for me. You probably read that I suffered from &lt;a href=&quot;https://drewdevault.com/blog/Burnout/&quot;&gt;burnout&lt;/a&gt; earlier in the year. In some respects, things have improved, and in many other respects, I am still haunted.&lt;/p&gt;&lt;p&gt;You might not care to read this, and so be it, take your leave if you must. But writing is healing for me. Maybe this is a moment for solidarity, sympathy, for reflecting on your own communities. Maybe it’s a vain and needlessly public demonstration of my slow descent into madness. I don’t know, but here we go.&lt;/p&gt;&lt;p&gt;Yesterday was my 30&lt;sup&gt;th&lt;/sup&gt; birthday. 🎂 It was another difficult day for me. I drafted a long blog post with all of the details of the events leading up to my burnout. You will never read it; I wrote it for myself and it will only be seen by a few confidants, in private, and my therapist. But I do want to give you an small idea of what I’ve been going through, and some of the take-aways that matter for you and the hacker community as a whole.&lt;/p&gt;&lt;p&gt;Here’s a quote from yesterday’s unpublished blog post:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Trigger warnings: child abuse, rape, sexual harassment, suicide, pedophilia, torture.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You won’t read the full story, and trust me, you’re better off for that. Suffice to say that my life has been consumed with trauma and strife all year. I have sought healing, and time for myself, time to process things, and each time a new crisis has landed on my doorstep, most of them worse than the last. A dozen things went wrong this year, horribly wrong, one after another. I have enjoyed no peace in 2023.&lt;/p&gt;&lt;p&gt;Many of the difficulties I have faced this year have been beyond the scope of the hacker community, but several have implicated it in challenging and confronting ways.&lt;/p&gt;&lt;p&gt;The hacker community has been the home I never had, but I’m not really feeling at home here right now. A hacker community that was precious to me failed someone I love and put my friends in danger. Rape and death had come to our community, and was kept silent. But I am a principled person, and I stand for what is right; I spoke the truth and it brought me and my loved ones agonizing stress and trauma and shook our community to the core. Board members resigned. Marriages are on the rocks. When the dust settled, I was initially uncomfortable staying in this community, but things eventually started to get better. Until another member of this community, someone I trusted and thought of as a friend, confessed to me that he had raped multiple women a few years ago. I submitted my resignation from this community last night.&lt;/p&gt;&lt;p&gt;Then I went to GPN, a hacker event in Germany, at the start of June. It was a welcome relief from the stress I’ve faced this year, a chance to celebrate hacker culture and a warm reminder of the beauty of our community. It was wonderful. Then, on the last night, a friend took me aside and confided in me that they are a pedophile, and told me it was okay because they respected the age of consent in Germany – which is 14. What began as a wonderful reminder of what the hacker community can be became a PTSD episode and a reminder that rape culture is fucking everywhere.&lt;/p&gt;&lt;p&gt;I don’t want to be a part of this anymore. Our communities have tolerated casual sexism and misogyny and transphobia and racism and actual fucking rapists, and stamped down on women and queer people and brown people in our spaces with a smile on our face and a fucked-up facsimile of tolerance and inclusion as a cornerstone of the hacker ethic.&lt;/p&gt;&lt;p&gt;This destroys communities. It is destroying &lt;em&gt;our&lt;/em&gt; communities. If there’s one thing I came to understand this year, it’s that these problems are &lt;em&gt;pervasive&lt;/em&gt; and &lt;em&gt;silent&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Here’s what you need to do: believe the victims. Stand up for what’s right. Have the courage to remove harmful people from your environment, especially if you’re a man and have a voice. Make people feel welcome, and seen. Don’t tolerate casual sexism in the hacker community or anywhere else. Don’t tolerate transphobia or homophobia. Don’t tolerate racists. If you see something, say something. And for fuck’s sake, don’t bitch about that code of conduct that someone wants to add to your community.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;I’m going to withdraw a bit from the in-person hacker community for the indefinite future. I don’t think I can manage it for a while. I have felt good about working on my software and collaborating with my free software communities online, albeit at a much-reduced capacity. I’m going to keep working, and writing, insofar as I find satisfaction in it. Life goes on.&lt;/p&gt;&lt;p&gt;Be there for the people you love, and love more people, and be there for them, too.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Burnout-2/</link>
        
        <pubDate>Thu, 29 Jun 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Burnout-2/</guid>
      </item>
    
      <item>
        
        
          <title>Reforming the free software message</title>
          <description>
            &lt;p&gt;Several weeks ago, I wrote &lt;a href=&quot;https://drewdevault.com/2023/04/11/2023-04-11-The-FSF-is-dying.html&quot; target=&quot;_blank&quot;&gt;The Free Software Foundation is dying&lt;/a&gt;, wherein I enumerated a number of problems with the Free Software Foundation. Some of my criticisms focused on the message: fsf.org and gnu.org together suffer from no small degree of incomprehensibility and inaccessibility which makes it difficult for new participants to learn about the movement and apply it in practice to their own projects.&lt;/p&gt;&lt;p&gt;This is something which is relatively easily fixed! I have a background in writing documentation and a thorough understanding of free software philosophy and practice. Enter &lt;a href=&quot;https://writefreesoftware.org&quot; target=&quot;_blank&quot;&gt;writefreesoftware.org&lt;/a&gt;: a comprehensive introduction to free software philosophy and implementation.&lt;/p&gt;&lt;p&gt;The goals of this resource are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Provide an accessible introduction to the most important principles of free software&lt;/li&gt;&lt;li&gt;Offer practical advice on choosing free software licenses from a free software perspective (compare to the OSS perspective at &lt;a href=&quot;https://choosealicense.com/&quot; target=&quot;_blank&quot;&gt;choosealicense.com&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;Publish articles covering various aspects of free software in practice, such as how it can be applied to &lt;a href=&quot;https://writefreesoftware.org/blog/free-software-games/&quot; target=&quot;_blank&quot;&gt;video games&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;More:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;No particular association with any particular free software project or organization&lt;/li&gt;&lt;li&gt;No policy of non-cooperation with the open source movement&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Compare &lt;a href=&quot;https://writefreesoftware.org&quot; target=&quot;_blank&quot;&gt;writefreesoftware.org&lt;/a&gt; with the similar resources provided by GNU (&lt;a href=&quot;https://www.gnu.org/licenses/license-recommendations.html&quot; target=&quot;_blank&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://www.gnu.org/philosophy/philosophy.html&quot; target=&quot;_blank&quot;&gt;2&lt;/a&gt;) and you should get the general idea.&lt;/p&gt;&lt;p&gt;The website is itself free software, CC-BY-SA 4.0. You can check out the &lt;a href=&quot;https://sr.ht/~sircmpwn/writefreesoftware.org/&quot; target=&quot;_blank&quot;&gt;source code here&lt;/a&gt; and suggest any improvements or articles for the mailing list. Get involved! This resource is not going to solve all of the FSF’s problems, but it is an easy way to start putting the effort in to move the free software movement forward. I hope you like it!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Reforming-the-free-software-message/</link>
        
        <pubDate>Mon, 19 Jun 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Reforming-the-free-software-message/</guid>
      </item>
    
      <item>
        
        
          <title>Throwing in the towel on mobile Linux</title>
          <description>
            &lt;p&gt;I have been tinkering with mobile Linux – a phrase I will use here to describe any Linux distribution other than Android running on a mobile device – as my daily driver since about 2019, when I first picked up the PinePhone. For about 3 years I have run mobile Linux as my daily driver on my phone, and as of a few weeks ago, I’ve thrown in the towel and switched to Android.&lt;/p&gt;&lt;p&gt;The distribution I ran for the most time is &lt;a href=&quot;https://postmarketos.org/&quot; target=&quot;_blank&quot;&gt;postmarketOS&lt;/a&gt;, which I was mostly quite happy with, running at times sxmo and Phosh. I switched to &lt;a href=&quot;https://ubports.com/en/&quot; target=&quot;_blank&quot;&gt;UBports&lt;/a&gt; a couple of months ago. I have tried a variety of hardware platforms to support these efforts, namely:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Pinephone (pmOS)&lt;/li&gt;&lt;li&gt;Pinephone Pro (pmOS)&lt;/li&gt;&lt;li&gt;Xiaomi Poco F1 (pmOS)&lt;/li&gt;&lt;li&gt;Fairphone 4 (UBports)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I have returned to LineageOS as my daily driver and closed the book on mobile Linux for the time being. What put the final nails in the coffin was what I have been calling out as my main concern throughout my experience: reliability, particularly of the telephony components.&lt;/p&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Use-case&lt;/th&gt;
      &lt;th&gt;Importance&lt;/th&gt;
      &lt;th&gt;postmarketOS&lt;/th&gt;
      &lt;th&gt;UBports&lt;/th&gt;
      &lt;th&gt;LineageOS&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Basic system reliability&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;2&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Mobile telephony&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hotspot&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2FA&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Web browsing&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;2&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Mobile banking&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Bluetooth audio&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;2&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Music player&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Reading email&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Navigation aid&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;2&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Camera&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Password manager&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;sysadmin&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;blue&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;red&quot;&gt;2&lt;/td&gt;
      &lt;td class=&quot;yellow&quot;&gt;3&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;details class=&quot;block&quot;&gt;&lt;summary&gt;More on these use-cases and my experiences&lt;/summary&gt;&lt;p&gt;&lt;strong&gt;Mobile banking&lt;/strong&gt;: only available through a proprietary vendor-provided Android app. Tried to get it working on Waydroid; did not work on pmOS and almost worked on UBports, but Waydroid is &lt;em&gt;very&lt;/em&gt; unreliable. Kind of shit but I don’t have any choice because my bank requires it for 2FA.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Web browsing&lt;/strong&gt;: I can just run Firefox upstream on postmarketOS. Amazing! UBports cannot do this, and the available web browsers are not nearly as pleasant to use. I run Fennic on Android and it’s fine.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Music player&lt;/strong&gt;: the music player on UBports is &lt;em&gt;extremely&lt;/em&gt; unreliable.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Reading email&lt;/strong&gt;: This is not entirely pmOS’s fault; I could have used my main client, aerc, which is a testament to pmOS’s general utility, but it is a TUI that is uncomfortable to use on a touchscreen-only device.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Password manager&lt;/strong&gt;: pmOS gets 5/5 because I could use the password manager I wrote myself, &lt;a href=&quot;https://himitsustore.org&quot; target=&quot;_blank&quot;&gt;himitsu&lt;/a&gt;, out of the box. Non-critical use-case because I could just type passwords in manually on the rare occasion I need to use one.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;sysadmin&lt;/strong&gt;: stuff like being able to SSH into my production boxes from anywhere to troubleshoot stuff.&lt;/p&gt;&lt;/details&gt;&lt;p&gt;Among these use-cases, there is one that absolutely cannot be budged on: mobile telephony. My phone is a critical communication device and I need to be able to depend on calls and SMS at all times, therefore the first two rows need to score 4 or 5 before the platform is suitable for my use. I remember struggling with postmarketOS while I was sick with a terrible throat infection – and I could not call my doctor. Not cool.&lt;/p&gt;&lt;p&gt;I really like these projects and I love the work that’s going into them. postmarketOS in particular: being able to run the same environment I run everywhere else, Alpine Linux, on my phone, is fucking amazing. The experience is impressively complete in many respects, all kinds of things, including things I didn’t expect to work well, work great. In the mobile Linux space I think it’s the most compelling option right now.&lt;/p&gt;&lt;p&gt;But pmOS really suffers from reliability issues – both on edge and on stable it seemed like every update broke some things and fixed others, so only a subset of these cool features was working well at any given moment. The breakage would often be minor nuisances, such as the media controls on my bluetooth headphones breaking in one update and being fixed in the next, or major showstoppers such as broken phone calls, SMS, or, in one case, all of my icons disappearing from the UI (with no fallback in most cases, leaving me navigating the UI blind).&lt;/p&gt;&lt;p&gt;So I tried UBports instead, and despite the general lack of good auxiliary features compared to pmOS, the core telephony was more reliable – for a while. But once issues started to appear, particularly around SMS, I could not tolerate it for long in view of the general uselessness of the OS for anything else. I finally gave it up and installed LineageOS.&lt;/p&gt;&lt;p&gt;Mobile Linux is very cool and the community has made tremendous, unprecedented progress towards realizing its potential, and the forward momentum is still strong. I’m excited to see it continue to improve. But I think that before anyone can be expected to use this as a daily driver, the community really needs to batten down the hatches and focus on one thing and one thing only: always, &lt;em&gt;always&lt;/em&gt; being usable as a phone. I’ll be back once more reliability is in place.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Mobile-linux-retrospective/</link>
        
        <pubDate>Fri, 16 Jun 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Mobile-linux-retrospective/</guid>
      </item>
    
      <item>
        
        
          <title>How to go to war with your employer</title>
          <description>
            &lt;p&gt;There is a power differential between you and your employer, but that doesn’t mean you can’t improve your working conditions. Today I’d like to offer a little bit of advice on how to frame your relationship with your employer in terms which empower you and afford you more agency. I’m going to talk about the typical working conditions of the average white-collar job in a neo-liberal political environment where you are mostly happy to begin with and financially stable enough to take risks, and I’m specifically going to talk about individual action or the actions of small groups rather than large-scale collective action (e.g. unions).&lt;/p&gt;&lt;p&gt;I wish to subvert the expectation here that employees are subordinate to their employers. A healthy employment relationship between an employee and employer is that of two entities who agree to work together on equal terms to strive towards mutual goals, which in the simplest form is that you both make money and in the subtleties also suggests that you should be happy doing it. The sense of “going to war” here should rouse in you an awareness of the resources at your disposal, a willingness to use them to forward your interests, and an acknowledgement of the fact that tactics, strategy, propaganda, and subterfuge are among the tools you can use – and the tools your employer uses to forward their own interests.&lt;/p&gt;&lt;p&gt;You may suppose that you need your employer more than they need you, but with some basic accounting we can get a better view of the veracity of this supposition. Consider at the most fundamental that your employer is a for-profit entity that spends money to make money, and they spend money on you: as a rule of thumb, they expect a return of at least your salary ×1.5 (accounting for overhead, benefits et al) for their investment in you, otherwise it does not make financial sense for them to employ you.&lt;/p&gt;&lt;p&gt;If you have finer-grained insights into your company’s financial situation, you can get a closer view of your worth to them by dividing their annual profit by their headcount, adjusted to your discretion to account for the difference in the profitability of your role compared to your colleagues. It’s also wise to run this math in your head to see how the returns from your employment are affected by conditions in the hiring market, layoffs, etc – having fewer employees increases the company’s return per employee, and a busier hiring market reduces your leverage. In any case, it should be relatively easy for you to justify, in the cold arithmetic of finance that businesses speak, that employees matter to the employer, and the degree to which solidarity between workers is a meaningful force amplifier for your leverage.&lt;/p&gt;&lt;p&gt;In addition to your fundamental value, there are some weak points in the corporate structure that you should be aware of. There are some big levers that you may already be aware of that I have already placed outside of the scope of this blog post, such as the use of collective bargaining, unionization, strikes, and so on, where you need to maximize your collective leverage to really put the screws to your employer. Many neo-liberal workplaces lack the class consciousness necessary to access these levers, and on the day-to-day scale it may be strategically wise to smarten up your colleagues on social economics in preparation for use of these levers. I want to talk about goals on the smaller scale, though. Suppose your goals are, for instance:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You don’t like agile/scrum and want to interact with it from the other end of a six foot pole and/or replace it with another system&lt;/li&gt;&lt;li&gt;Define your own goals and work on the problems you think are important at your own discretion moreso than at the discretion of your manager&lt;/li&gt;&lt;li&gt;Skip meetings you know are wasting your time&lt;/li&gt;&lt;li&gt;Set working hours that suit you or take time off on your terms&lt;/li&gt;&lt;li&gt;Work from home or in-office in an arrangement that meets your own wants/needs&lt;/li&gt;&lt;li&gt;Exercise agency over your tools, such as installing the software you want to use on your work laptop&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You might also have more intimidating goals you want to address:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Demand a raise or renegotiating benefits&lt;/li&gt;&lt;li&gt;Negotiate a 4-day workweek&lt;/li&gt;&lt;li&gt;Replace your manager or move teams&lt;/li&gt;&lt;li&gt;Remove a problematic colleague from your working environment&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;All of these goals are within your power to achieve, and perhaps more easily than you expect.&lt;/p&gt;&lt;p&gt;First of all, you already have more agency than you know. Your job description and assigned tasks tells a narrow story of your role at the business: your real job is ultimately to make money for the business. If you install Linux on your work laptop because it allows you to work more efficiently, then you are doing your job better and making more money for the business; they have no right to object to this and you have a very defensible position for exercising agency in this respect. Likewise if you adapt the workflows around agile (or whatever) to better suit your needs rather than to fall in line with the prescription, if it makes you more productive and happy then it makes the business more money. Remember your real job – to make money – and you can adjust the parameters of your working environment relatively freely provided that you are still aligned with this goal.&lt;/p&gt;&lt;p&gt;Often you can simply exercise agency in cases like these, but in other cases you may have to reach for your tools. Say you don’t just want to have maintain a personal professional distance from agile, but you want to replace it entirely: now you need to talk to your colleagues. You can go straight to management and start making your case, but another option – probably the more effective one – is to start with your immediate colleagues. Your team also possesses a collective agency, and if you agree together, without anyone’s permission, to work according to your own terms, then so long as you’re all doing your jobs – making money – then no one is going to protest. This is more effective than following the chain of command and asking them to take risks they don’t understand. Be aware of the importance of optics here: you need not only to make money, but to be &lt;em&gt;seen&lt;/em&gt; making money. How you are seen to be doing this may depend on how far up the chain you need to justify yourself to; if your boss doesn’t like it then make sure your boss’s boss does.&lt;/p&gt;&lt;p&gt;Ranked in descending order of leverage within the business: your team, your boss, you.&lt;/p&gt;&lt;p&gt;More individual-oriented goals such as negotiating a different working schedule or skipping meetings calls for different tools. Simple cases, such as coming in at ten and leaving at four every day, are a case of simple exercise of agency; so long as you’re making the company money no one is going to raise a fuss. If you want, for instance, a four day work-week, or to work from home more often, you may have to justify yourself to someone. In such cases you may be less likely to have your team’s solidarity at your disposal, but if you’re seen to be doing your job – making money – then a simple argument that it makes you better at that job will often suffice.&lt;/p&gt;&lt;p&gt;You can also be clever. “Hey, I’ll be working from home on Friday” works better than “can I work from home on Friday?” If you want to work from home &lt;em&gt;every&lt;/em&gt; Friday, however, then you can think strategically: keeping mum about your final goal of taking all Fridays from home may be wise if you can start by taking &lt;em&gt;some&lt;/em&gt; Fridays at home to establish that you’re still productive and fulfilling the prime directive&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; under those terms and allow yourself to “accidentally” slip into a new normal of working home every Friday without asking until it’s apparent that the answer will be yes. Don’t be above a little bit of subversion and deception; your employer is using those tools against you too.&lt;/p&gt;&lt;p&gt;Then there are the big guns: human resources. HR is the enemy; their job is to protect the company from you. They can, however, be useful if you understand the risks they’re trying to manage and press the right buttons with them. If your manager is a dick, HR may be the tool to use to fix this, but you need to approach it the right way. HR does not give two fucks that you don’t like your manager, if your manager is making money then they are doing their job. What HR does give a fuck about is managing the company’s exposure to lawsuits.&lt;/p&gt;&lt;p&gt;They can also make your life miserable. If HR does not like you then you are going to suffer, so when you talk to them it is important to know your enemy and to make strategic use of them without making them realize you know the game. They present themselves as your ally, let them think you believe it’s so. At the same time, there is a coded language you can use that will get them to act in your interest. HR will perk up as soon as they smell “unsafe working conditions”, “sexual harassment”, “collective action”, and so on – the risks they were hired to manage – over the horizon. The best way to interact with HR is for them to conclude that you are on a path which ends in these problems landing on their desk without making them think you are a subversive element within the organization. And if you are prepared to make your knowledge of and willingness to use these tools explicit, all communication which suggests as much should be delivered to HR with your lawyer’s signature and only when you have a new job offer lined up as a fallback. HR should either view you as mostly harmless or look upon you with fear, but nothing in between.&lt;/p&gt;&lt;p&gt;These are your first steps towards class consciousness as a white-collar employee. Know your worth, know the leverage you have, and be prepared to use the tools at your disposal to bring about the outcomes you desire, and know your employer will be doing the same. Good luck out there, and don’t forget to actually write some code or whatever when you’re not busy planning a corporate coup.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-go-to-war/</link>
        
        <pubDate>Mon, 12 Jun 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-go-to-war/</guid>
      </item>
    
      <item>
        
        
          <title>Burnout</title>
          <description>
            &lt;p&gt;It kind of crept up on me. One day, sitting at my workstation, I stopped typing, stared blankly at the screen for a few seconds, and a switch flipped in my head.&lt;/p&gt;&lt;p&gt;On the night of New Year’s Eve, my backpack was stolen from me on the train from Berlin to Amsterdam, and with it about $2000 worth of equipment, clothes, and so on. A portent for the year that was to come. I generally keep my private and public lives carefully separated, but perhaps I will offer you a peek behind the curtain today.&lt;/p&gt;&lt;p&gt;It seems like every week or two this year, another crisis presented itself, each manageable in isolation. Some were independent events, others snowballed as the same problems escalated. Gossip at the hackerspace, my personal life put on display and mocked. A difficult break-up in February, followed by a close friend facing their own relationship’s hurtful end. Another close friend – old, grave problems, once forgotten, remembered, and found to still be causing harm. Yet another friend, struggling to deal with depression and emotional abuse at the hands of their partner. Another friendship still: lost, perhaps someday to be found again.&lt;/p&gt;&lt;p&gt;Dependable Drew, an ear to listen, a shoulder to cry on, always knowing the right words to say, ready to help and proud to be there for his friends. Friends who, amidst these crises, are struggling to be there for him.&lt;/p&gt;&lt;p&gt;These events, set over the background of a world on fire.&lt;/p&gt;&lt;p&gt;One of the more difficult crises in my purview reached its crescendo one week ago, culminating in death. A selfish end for a selfish person, a person who had hurt people I love; a final, cruel cut to the wounds we were trying to heal.&lt;/p&gt;&lt;p&gt;I took time for myself throughout these endless weeks, looked after myself as best I could, and allowed my productivity to wane as necessary, unburdened by guilt in so doing. I marched on when I had the energy to, and made many achievements I’m proud of.&lt;/p&gt;&lt;p&gt;Something changed this week. I have often remarked that when you’re staring down a hard problem, one which might take years or even decades to finish, that you have two choices: give up or get to work. The years are going to pass either way. I am used to finding myself at the base of a mountain, picking up my shovel, and getting started. Equipped with this mindset, I have patiently ground down more than one mountain in my time. But this week, for the first time in my life, as I gazed upon that mountain, I felt intimidated.&lt;/p&gt;&lt;p&gt;I’m not sure what the purpose of this blog post is. Perhaps I’m sharing an experience that others might be able to relate to. Perhaps it’s healing in some way. Maybe it’s just indulgent.&lt;/p&gt;&lt;p&gt;I’m going to take the time I need to rest. I enjoy the company of wonderful colleagues at SourceHut, who have been happy to pick up some of the slack. I have established a formal group of maintainers for Hare and given them my blessing to work without seeking my approval. My projects will remain healthy as I take a leave. See you soon.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Burnout/</link>
        
        <pubDate>Mon, 01 May 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Burnout/</guid>
      </item>
    
      <item>
        
        
          <title>Who should lead us?</title>
          <description>
            &lt;p&gt;Consider these two people, each captured in the midst of delivering a technical talk.&lt;/p&gt;&lt;div class=&quot;images&quot;&gt;
  &lt;img src=&quot;https://redacted.moe/f/c34dbc20.jpg&quot; alt=&quot;A picture of a young trans woman in a red dress&quot;&gt;
  &lt;img src=&quot;https://redacted.moe/f/bd64d141.jpg&quot; alt=&quot;A picture of a middle-aged white man in a red shirt&quot;&gt;
&lt;/div&gt;
&lt;style&gt;.images { display: flex; }&lt;/style&gt;
&lt;p&gt;Based on appearances alone, what do you think of them?&lt;/p&gt;&lt;p&gt;The person on the left is a woman. She’s also pretty young, one might infer something about her level of experience accordingly. I imagine that she has led a much different life than I have, and may have a much different perspective, worldview, identity, and politics than I. Does she complain about sexism and discrimination in her work? Is she a feminist? Does she lean left or right on the political spectrum?&lt;/p&gt;&lt;p&gt;The person on the right looks like most of the hackers I’ve met. You’ve met someone who looks like this a thousand times. He is a man, white and middle-aged – that suggests a fair bit of experience. He probably doesn’t experience or concern himself with race or gender discrimination in the course of his work. He just focuses on the software. His life experiences probably map relatively well onto my own, and we may share a similar worldview and identity.&lt;/p&gt;&lt;p&gt;Making these assumptions is a part of human nature – it’s a useful shortcut in many situations. But they are assumptions based only on appearances. What are the facts?&lt;/p&gt;&lt;p&gt;The person on the right is Scott Guthrie, Vice President of Cloud and AI at Microsoft, giving a talk about Azure’s cloud services. He lives in an $11M house in Hunts Point, Washington. On the left is Alyssa Rosenzweig, main developer for the free software Panfrost GPU drivers and a trans woman, talking about how she reverse engineers proprietary graphics hardware.&lt;/p&gt;&lt;p&gt;You and I have a lot more in common with Alyssa than with Scott.  The phone I have in my pocket right now would not work without her drivers. Alyssa humbles me with her exceptional talent and dedication, and the free software community is indebted to her. If you use ARM devices with free software, you owe something to Alyssa. As recently as February, her Wikipedia page was vandalized by someone who edited “she” and “her” to “he” and “him”.&lt;/p&gt;&lt;p&gt;Appearances should not especially matter when considering the merit of someone considered for a leadership role in our community, be it as a maintainer, thought leader, member of our foundations’ boards, etc. I am myself a white man, and I think I perform well in my leadership roles throughout the free software ecosystem. But it’s not my appearance that causes any controversy: someone with the approximate demographic shape of myself or Guthrie would cause no susurration when taking the stage.&lt;/p&gt;&lt;p&gt;It’s those like Alyssa, who aside from anything else is eminently qualified and well-deserving of her leadership role, who are often the target of ire and discrimination in the community. This is an experience shared by many people whose gender expression, skin color, or other traits differ from the “norm”. They’ve been telling us so for years.&lt;/p&gt;&lt;p&gt;Is it any wonder that our community is predominantly made up of white cisgendered men when anyone else is ostracized? It’s not because we’re predisposed to be better at this kind of work. It’s patently absurd to suppose that hackers whose identities and life experience differ from yours or mine cannot be good participants in and leaders of our movement. In actual fact, diverse teams produce better results. While the labor pool is disproportionately filled with white men, we can find many talented hackers who cannot be described as such. If we choose to be inspired by them, and led by them, we will discover new perspectives on our software, and on our movement and its broader place in the world. They can help us create a safe and inviting space for other talented hackers who identify with them. We will be more effective at our mission of bringing free software to everyone with their help.&lt;/p&gt;&lt;p&gt;Moreover, there are a lot of damned good hackers who don’t look like me, and I would be happy to follow their lead regardless of any other considerations.&lt;/p&gt;&lt;p&gt;The free software ecosystem (and the world at large) is not under threat from some woke agenda – a conspiracy theory which has been fabricated out of whole cloth. The people you fear are just people, much like you and I, and they only want to be treated as such.  Asking them to shut up and get in line, to suppress their identity, experiences, and politics, to avoid confronting you with uncomfortable questions about your biases and privileges by way of their existence alone – it’s not right.&lt;/p&gt;&lt;p&gt;Forget the politics and focus on the software? It’s simply not possible. Free software &lt;em&gt;is&lt;/em&gt; politics. Treating other people with respect, maturity, and professionalism, and valuing their contributions at any level, including leadership, regardless of their appearance or identity – that’s just part of being a good person. &lt;em&gt;That&lt;/em&gt; is apolitical.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;Alyssa gave her blessing regarding the use of her image and her example in this post. Thanks!&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Who-leads-us/</link>
        
        <pubDate>Mon, 24 Apr 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Who-leads-us/</guid>
      </item>
    
      <item>
        
        
          <title>rc: a new shell for Unix</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/rc&quot; target=&quot;_blank&quot;&gt;rc&lt;/a&gt; is a Unix shell I’ve been working on over the past couple of weeks, though it’s been in the design stages for a while longer than that. It’s not done or ready for general use yet, but it is interesting, so let’s talk about it.&lt;/p&gt;&lt;p&gt;As the name (which is subject to change) implies, rc is inspired by the Plan 9 &lt;a href=&quot;http://man.9front.org/1/rc&quot; target=&quot;_blank&quot;&gt;rc&lt;/a&gt; shell. It’s not an implementation of Plan 9 rc, however: it departs in many notable ways. I’ll assume most readers are more familiar with POSIX shell or Bash and skip many of the direct comparisons to Plan 9. Also, though most of the features work as described, the shell is a work-in-progress and some of the design I’m going over today has not been implemented yet.&lt;/p&gt;&lt;p&gt;Let’s start with the basics. Simple usage works much as you’d expect:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;name=ddevault
echo Hello $name
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But there’s already something important that might catch your eye here: the lack of quotes around $name. One substantial improvement rc makes over POSIX shells and Bash right off the bat is fixing our global shell quoting nightmare. There’s no need to quote variables!&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# POSIX shell
x=&amp;quot;hello world&amp;quot;
printf &amp;apos;%s\n&amp;apos; $x
# hello
# world

# rc
x=&amp;quot;hello world&amp;quot;
printf &amp;apos;%s\n&amp;apos; $x
# hello world
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Of course, the POSIX behavior is actually useful sometimes. rc provides for this by acknowledging that shells have not just one fundamental type (strings), but two: strings and &lt;em&gt;lists&lt;/em&gt; of strings, i.e. argument vectors.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;x=(one two three)
echo $x(1)  # prints first item (&amp;quot;one&amp;quot;)
echo $x     # expands to arguments (echo &amp;quot;one&amp;quot; &amp;quot;two&amp;quot; &amp;quot;three&amp;quot;)
echo $#x    # length operator: prints 3

x=&amp;quot;echo hello world&amp;quot;
$x
# echo hello world: command not found

x=(echo hello world)
$x
# hello world

# expands to a string, list values separated with space:
$&amp;quot;x
# echo hello world: command not found
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also slice up lists and get a subset of items:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;x=(one two three four five)
echo $x(-4) # one two three four
echo $x(2-) # two three four five
echo $x(2-4) # two three four
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A departure from Plan 9 rc is that the list operators can be used with strings for string operations as well:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;x=&amp;quot;hello world&amp;quot;
echo $#x     # 11
echo $x(2)   # e
echo $x(1-5) # hello
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;rc also supports loops. The simple case is iterating over the command line arguments:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;% cat test.rc 
for (arg) {
	echo $arg
}
% rc test.rc one two three 
one
two
three
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;{ } is a command like any other; this can be simplified to for (arg) echo $arg. You can also enumerate any list with in:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;list=(one two three)
for (item in $list) {
	echo $item
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We also have while loops and if:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;while (true) {
	if (test $x -eq 10) {
		echo ten
	} else {
		echo $x
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Functions are defined like so:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;fn greet {
	echo Hello $1
}

greet ddevault
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, any command can be used, so this can be simplified to fn greet echo $1. You can also add named parameters:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;fn greet(user time) {
	echo Hello $user
	echo It is $time
}

greet ddevault `{date}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the use of `{script…} instead of $() for command expansion. Additional arguments are still placed in $*, allowing for the user to combine variadic-style functions with named arguments.&lt;/p&gt;&lt;p&gt;Here’s a more complex script that I run to perform sanity checks before applying patches:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#!/bin/rc
fn check_branch(branch) {
	if (test `{git rev-parse --abbrev-ref HEAD} != $branch) {
		echo &amp;quot;Error: not on master branch&amp;quot;
		exit 1
	}
}

fn check_uncommitted {
	if (test `{git status -suno | wc -l} -ne 0) {
		echo &amp;quot;Error: you have uncommitted changes&amp;quot;
		exit 1
	}
}

fn check_behind {
	if (test `{git rev-list &amp;quot;@{u}..&amp;quot; | wc -l} -ne 0) {
		echo &amp;quot;Error: your branch is behind upstream&amp;quot;
		exit 1
	}
}

check_branch master
check_uncommitted
check_behind
exec git pull
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That’s a brief introduction to rc! Presently it clocks in at about 2500 lines of Hare. It’s not done yet, so don’t get too excited, but much of what’s described here is already working. Some other stuff which works but I didn’t mention include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Boolean compound commands (x &amp;&amp; y, x || y)&lt;/li&gt;&lt;li&gt;Pipelines, which can pipe arbitrary file descriptors (“x |[2] y”)&lt;/li&gt;&lt;li&gt;Redirects, also including arbitrary fds (“x &gt;[2=1] file”)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It also has a &lt;a href=&quot;https://git.sr.ht/~sircmpwn/rc/tree/master/item/doc/grammar.txt&quot; target=&quot;_blank&quot;&gt;formal context-free grammar&lt;/a&gt;, which is a work-in-progress but speaks to our desire to have a robust description of the shell available for users and other implementations. We use Ember Sawady’s excellent &lt;a href=&quot;https://git.d2evs.net/~ecs/madeline/&quot; target=&quot;_blank&quot;&gt;madeline&lt;/a&gt; for our interactive mode, which supports command line editing, history, ^r, and fish-style forward completion OOTB.&lt;/p&gt;&lt;p&gt;Future plans include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Simple arithmetic expansion&lt;/li&gt;&lt;li&gt;Named pipe expansions&lt;/li&gt;&lt;li&gt;Sub-shells&lt;/li&gt;&lt;li&gt;switch statements&lt;/li&gt;&lt;li&gt;Port to &lt;a href=&quot;https://ares-os.org&quot; target=&quot;_blank&quot;&gt;ares&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Find a new name, perhaps&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It needs a small amount of polish, cleanup, and bugs fixed as well.&lt;/p&gt;&lt;p&gt;I hope you find it interesting! I will let you know when it’s done. Feel free to &lt;a href=&quot;https://git.sr.ht/~sircmpwn/rc&quot; target=&quot;_blank&quot;&gt;play with it&lt;/a&gt; in the meanwhile, and maybe send some patches?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-new-shell-for-Unix/</link>
        
        <pubDate>Tue, 18 Apr 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-new-shell-for-Unix/</guid>
      </item>
    
      <item>
        
        
          <title>The Free Software Foundation is dying</title>
          <description>
            &lt;p&gt;The Free Software Foundation is one of the longest-running missions in the free software movement, effectively defining it. It provides a legal foundation for the movement and organizes activism around software freedom. The GNU project, closely related, has its own long story in our movement as the coding arm of the Free Software Foundation, taking these principles and philosophy into practice by developing free software; notably the GNU operating system that famously rests atop GNU/Linux.&lt;/p&gt;&lt;p&gt;Today, almost 40 years on, the FSF is dying.&lt;/p&gt;&lt;p&gt;Their achievements are unmistakable: we must offer them our gratitude and admiration for decades of accomplishments in establishing and advancing our cause. The principles of software freedom are more important than ever, and the products of these institutions remain necessary and useful – the GPL license family, GCC, GNU coreutils, and so on. Nevertheless, the organizations behind this work are floundering.&lt;/p&gt;&lt;p&gt;The Free Software Foundation must concern itself with the following ahead of all else:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Disseminating free software philosophy&lt;/li&gt;&lt;li&gt;Developing, publishing, and promoting copyleft licenses&lt;/li&gt;&lt;li&gt;Overseeing the health of the free software movement&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;It is failing in each of these regards, and as its core mission fails, the foundation is investing its resources into distractions.&lt;/p&gt;&lt;p&gt;In its role as the thought-leaders of free software philosophy, the message of the FSF has a narrow reach. The organization’s messaging is tone-deaf, ineffective, and myopic. Hammering on about “GNU/Linux” nomenclature, antagonism towards our allies in the open source movement, maligning the audience as “useds” rather than “users”; none of this aids the cause. The pages and pages of dense philosophical essays and poorly organized FAQs do not provide a useful entry point or reference for the community. The message cannot spread like this.&lt;/p&gt;&lt;p&gt;As for copyleft, well, it’s no coincidence that many people struggle with the FSF’s approach. Do you, dear reader, know the difference between free software and copyleft? Many people assume that the MIT license is not free software because it’s not viral. The GPL family of licenses are essential for our movement, but few people understand its dense and esoteric language, despite the 16,000-word FAQ which supplements it. And hip new software isn’t using copyleft: over 1 million npm packages use a permissive license while fewer than 20,000 use the GPL; cargo sports a half-million permissive packages and another 20,000 or so GPL’d.&lt;/p&gt;&lt;p&gt;And is the free software movement healthy? This one gets an emphatic “yes!” – thanks to the open source movement and the near-equivalence between free software and open source software. There’s more free software than ever and virtually all new software contains free software components, and most people call it open source.&lt;/p&gt;&lt;p&gt;The FOSS community is now dominated by people who are beyond the reach of the FSF’s message. The broader community is enjoying a growth in the diversity of backgrounds and values represented, and the message does not reach these people. The FSF fails to understand its place in the world as a whole, or its relationship to the progressive movements taking place in the ecosystem and beyond. The foundation does not reach out to new leaders in the community, leaving them to form insular, weak institutions among themselves with no central leadership, and leaving us vulnerable to exploitation from growing movements like open core and commercial attacks on the free and open source software brand.&lt;/p&gt;&lt;p&gt;Reforms are sorely needed for the FSF to fulfill it basic mission. In particular, I call for the following changes:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Reform the leadership&lt;/strong&gt;. It’s time for Richard Stallman to go. His polemeic rhetoric rivals even my own, and the demographics he represents – to the exclusion of all others – is becoming a minority within the free software movement. We need more leaders of color, women, LGBTQ representation, and others besides. The present leadership, particularly from RMS, creates an exclusionary environment in a place where inclusion and representation are important for the success of the movement.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Reform the institution&lt;/strong&gt;. The FSF needs to correct its myopic view of the ecosystem, reach out to emerging leaders throughout the FOSS world, and ask them to take charge of the FSF’s mission. It’s these leaders who hold the reins of the free software movement today – not the FSF. If the FSF still wants to be involved in the movement, they need to recognize and empower the leaders who are pushing the cause forward.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Reform the message&lt;/strong&gt;. People depend on the FSF to establish a strong background in free software philosophy and practices within the community, and the FSF is not providing this. The message needs to be made much more accessible and level in tone, and the relationship between free software and open source needs to be reformed so that the FSF and OSI stand together as the pillars at the foundations of our ecosystem.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Decouple the FSF from the GNU project&lt;/strong&gt;. FSF and GNU have worked hand-in-hand over decades to build the movement from scratch, but their privileged relationship has become obsolete. The GNU project represents a minute fraction of the free software ecosystem today, and it’s necessary for the Free Software Foundation to stand independently of any particular project and focus on the health of the ecosystem as a whole.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Develop new copyleft licenses&lt;/strong&gt;. The GPL family of licenses has served us well, but we need to do better. The best copyleft license today is the &lt;a href=&quot;https://www.mozilla.org/en-US/MPL/&quot; target=&quot;_blank&quot;&gt;MPL&lt;/a&gt;, whose terse form and accessible language outperforms the GPL in many respects. However, it does not provide a comprehensive answer to the needs of copyleft, and new licenses are required to fill other niches in the market – the FSF should write these licenses. Furthermore, the FSF should present the community with a free software perspective on licenses as a resource that project leaders can depend on to understand the importance of their licensing choice such that they understand the appeal of copyleft licenses without feeling pushed away from permissive approaches.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The free software movement needs a strong force uniting it: we face challenges from many sides, and today’s Free Software Foundation is not equal to the task. The FOSS ecosystem is flourishing, and it’s time for the FSF to step up to the wheel and direct its coming successes in the name of software freedom.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-FSF-is-dying/</link>
        
        <pubDate>Tue, 11 Apr 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-FSF-is-dying/</guid>
      </item>
    
      <item>
        
        
          <title>Writing Helios drivers in the Mercury driver environment</title>
          <description>
            &lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Helios&lt;/a&gt; is a microkernel written in the &lt;a href=&quot;https://harelang.org&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt; programming language and is part of the larger &lt;a href=&quot;https://ares-os.org&quot; target=&quot;_blank&quot;&gt;Ares&lt;/a&gt; operating system. You can watch my FOSDEM 2023 talk introducing Helios &lt;a href=&quot;https://spacepub.space/w/wpKXfhqqr7FajEAf4B2Vc2&quot; target=&quot;_blank&quot;&gt;on PeerTube&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Let’s take a look at the new Mercury driver development environment for Helios.&lt;/p&gt;&lt;p&gt;As you may remember from my FOSDEM talk, the Ares operating system is built out of several layers which provide progressively higher-level environments for an operating system. At the bottom is the Helios microkernel, and today we’re going to talk about the second layer: the &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mercury&quot; target=&quot;_blank&quot;&gt;Mercury&lt;/a&gt; environment, which is used for writing and running device drivers in userspace. Let’s take a look at a serial driver written against Mercury and introduce some of the primitives used by driver authors in the Mercury environment.&lt;/p&gt;&lt;p&gt;Drivers for Mercury are written as normal ELF executables with an extra section called .manifest, which includes a file similar to the following (the provided example is for the serial driver we’ll be examining today):&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;ini&quot;&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;pcserial&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;Serial&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;driver&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;x86_64&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;PCs&lt;/span&gt;

&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;capabilities&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;0&lt;/span&gt;:&lt;span class=&quot;property&quot;&gt;ioport&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;F8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;400&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;1&lt;/span&gt;:&lt;span class=&quot;property&quot;&gt;ioport&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2E8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;F0&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;2&lt;/span&gt;:&lt;span class=&quot;property&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; 
&lt;span class=&quot;property&quot;&gt;3&lt;/span&gt;:&lt;span class=&quot;property&quot;&gt;irq&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;4&lt;/span&gt;:&lt;span class=&quot;property&quot;&gt;irq&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;_&lt;/span&gt;:&lt;span class=&quot;property&quot;&gt;cspace&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;self&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;_&lt;/span&gt;:&lt;span class=&quot;property&quot;&gt;vspace&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;self&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;_&lt;/span&gt;:&lt;span class=&quot;property&quot;&gt;memory&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;32&lt;/span&gt;

&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;devregistry&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Helios uses a capability-based design, in which access to system resources (such as I/O ports, IRQs, or memory) is governed by capability objects. Each process has a &lt;em&gt;capability space&lt;/em&gt;, which is a table of capabilities assigned to that process, and when performing operations (such as writing to an I/O port) the user provides the index of the desired capability in a register when invoking the appropriate syscall.&lt;/p&gt;&lt;p&gt;The manifest first specifies a list of capabilities required to operate the serial port. It requests, assigned static capability addresses, capabilities for the required I/O ports and IRQs, as well as a notification object which the IRQs will be delivered to. Some capability types, such as I/O ports, have configuration parameters, in this case the minimum and maximum port numbers which are relevant. The IRQ capabilities require a reference to a notification as well.&lt;/p&gt;&lt;p&gt;Limiting access to these capabilities provides very strong isolation between device drivers. On a monolithic kernel like Linux, a bug in the serial driver could compromise the entire system, but a vulnerability in our driver could, at worst, write garbage to your serial port. This model also provides better security than something like OpenBSD’s pledge by declaratively specifying what we need and nothing else.&lt;/p&gt;&lt;p&gt;Following the statically allocated capabilities, we request our own capability space and virtual address space, the former so we can copy and destroy our capabilities, and the latter so that we can map shared memory to perform reads and writes for clients. We also request 32 pages of memory, which we use to allocate page tables to perform those mappings; this will be changed later. These capabilities do not require any specific address for the driver to work, so we use “_” to indicate that any slot will suit our needs.&lt;/p&gt;&lt;p&gt;Mercury uses some vendor extensions over the System-V ABI to communicate information about these capabilities to the runtime. Notes about each of the _’d capabilities are provided by the auxiliary vector, and picked up by the Mercury runtime – for instance, the presence of a memory capability is detected on startup and is used to set up the allocator; the presence of a vspace capability is automatically wired up to the mmap implementation.&lt;/p&gt;&lt;p&gt;Each of these capabilities is implemented by the kernel, but additional services are available in userspace via endpoint capabilities. Each of these endpoints implements a particular API, as defined by a protocol definition file. This driver requires access to the device registry, so that it can create devices for its serial ports and expose them to clients.&lt;/p&gt;&lt;p&gt;These protocol definitions are written in a domain-specific language and parsed by &lt;a href=&quot;https://git.sr.ht/~sircmpwn/ipcgen&quot; target=&quot;_blank&quot;&gt;ipcgen&lt;/a&gt; to generate client and server implementations of each. Here’s a simple protocol to start us off:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;namespace io;

# The location with respect to which a seek operation is performed.
enum whence {
	# From the start of the file
	SET,
	# From the current offset
	CUR,
	# From the end of the file
	END,
};

# An object with file-like semantics.
interface file {
	# Reads up to amt bytes of data from a file.
	call read{pages: page...}(buf: uintptr, amt: size) size;

	# Writes up to amt bytes of data to a file.
	call write{pages: page...}(buf: uintptr, amt: size) size;

	# Seeks a file to a given offset, returning the new offset.
	call seek(offs: i64, w: whence) size;
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each interface includes a list of methods, each of which can take a number of capabilities and parameters, and return a value. The “read” call here, when implemented by a file-like object, accepts a list of memory pages to perform the read or write with (shared memory), as well as a pointer to the buffer address and size. Error handling is still a to-do.&lt;/p&gt;&lt;p&gt;ipcgen consumes these files and writes client or server code as appropriate. These are generated as part of the Mercury build process and end up in *_gen.ha files. The generated client code is filed away into the relevant modules (this protocol ends up at io/file_gen.ha), alongside various hand-written files which provide additional functionality and often wrap the IPC calls in a higher-level interface. The server implementations end up in the “serv” module, e.g. serv/io/file_gen.ha.&lt;/p&gt;&lt;p&gt;Let’s look at some of the generated client code for io::file objects:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// This file was generated by ipcgen; do not modify by hand&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// ID for the file IPC interface.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;FILE_ID&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x9A533BB3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Labels for operations against file objects.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;file_label&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_ID&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;16u64&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;WRITE&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_ID&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;16u64&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SEEK&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_ID&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;16u64&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;file_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each interface has a unique ID (generated from the FNV-1a hash of its fully qualified name), which is bitwise-OR’d with a list of operations to form call labels. The interface ID is used elsewhere; we’ll refer to it again later. Then each method generates an implementation which arranges the IPC details as necessary and invokes the “call” syscall against the endpoint capability.&lt;/p&gt;&lt;p&gt;The generated server code is a bit more involved. Some of the details are similar – FILE_ID is generated again, for instance – but there are some additional details as well. First is the generation of a vtable defining the functions implementing each operation:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Implementation of a [[file]] object.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;file_iface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_file_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_file_write&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_file_seek&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also define a file object which is subtyped by the implementation to store implementation details, and which provides to the generated code the required bits of state.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Instance of an file object. Users may subtype this object to add&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// instance-specific state.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;_iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;file_iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;_endpoint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s an example of a subtype of file used by the initramfs to store additional state:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// An open file in the bootstrap filesystem&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;bfs_file&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;serv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;bfs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;cur&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The embedded serv::io::file structure here is populated with an implementation of file_iface, here simplified for illustrative purposes:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;bfs_file_impl&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serv_io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;file_iface&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bfs_file_read&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bfs_file_write&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bfs_file_seek&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bfs_file_read&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;serv_io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; size &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;bfs_file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;PAGEMASK&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;error constant variable&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;map_flags&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;W&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// Not shown: reading the file data into this buffer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementation can prepare a file object and call dispatch on it to process client requests: this function blocks until a request arrives, decodes it, and invokes the appropriate function. Often this is incorporated into an event loop with poll to service many objects at once.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Prepare a file object&lt;/span&gt;
&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;newendpoint&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bfs_file&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_iface&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bfs_file_impl&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_endpoint&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cur&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Process requests associated with this file&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;serv&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;file_dispatch&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, enough background: back to the serial driver. It needs to implement the following protocol:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;namespace dev;
use io;

# TODO: Add busy error and narrow semantics

# Note: TWO is interpreted as 1.5 for some char lengths (5)
enum stop_bits {
	ONE,
	TWO,
};

enum parity {
	NONE,
	ODD,
	EVEN,
	MARK,
	SPACE,
};

# A serial device, which implements the file interface for reading from and
# writing to a serial port. Typical implementations may only support one read
# in-flight at a time, returning errors::busy otherwise.
interface serial :: io::file {
	# Returns the baud rate in Hz.
	call get_baud() uint;

	# Returns the configured number of bits per character.
	call get_charlen() uint;

	# Returns the configured number of stop bits.
	call get_stopbits() stop_bits;

	# Returns the configured parity setting.
	call get_parity() parity;

	# Sets the baud rate in Hz.
	call set_baud(hz: uint) void;

	# Sets the number of bits per character. Must be 5, 6, 7, or 8.
	call set_charlen(bits: uint) void;

	# Configures the number of stop bits to use.
	call set_stopbits(bits: stop_bits) void;

	# Configures the desired parity.
	call set_parity(parity: parity) void;
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This protocol &lt;em&gt;inherits&lt;/em&gt; the io::file interface, so the serial port is usable like any other file for reads and writes. It additionally defines serial-specific methods, such as configuring the baud rate or parity. The generated interface we’ll have to implement looks something like this, embedding the io::file_iface struct:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;serial_iface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;file_iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;get_baud&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_serial_get_baud&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;get_charlen&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_serial_get_charlen&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;get_stopbits&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_serial_get_stopbits&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;get_parity&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_serial_get_parity&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;set_baud&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_serial_set_baud&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;set_charlen&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_serial_set_charlen&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;set_stopbits&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_serial_set_stopbits&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;set_parity&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_serial_set_parity&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Time to dive into the implementation. Recall the driver manifest, which provides the serial driver with a suitable environment:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[driver]
name=pcserial
desc=Serial driver for x86_64 PCs

[capabilities]
0:ioport = min=3F8, max=400
1:ioport = min=2E8, max=2F0
2:note = 
3:irq = irq=3, note=2
4:irq = irq=4, note=2
_:cspace = self
_:vspace = self
_:memory = pages=32

[services]
devregistry=
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I/O ports for reading and writing to the serial devices, IRQs for receiving serial-related interrupts, a device registry to add our serial devices to the system, and a few extra things for implementation needs. Some of these are statically allocated, some of them are provided via the auxiliary vector. Our &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mercury/tree/5e12977a0cb773331b9b3b8421da63b85eed232c/item/cmd/serial&quot; target=&quot;_blank&quot;&gt;serial driver&lt;/a&gt; opens by defining constants for the statically allocated capabilities:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOPORT_A&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOPORT_B&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IRQ&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IRQ3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IRQ4&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing we do on startup is create a serial device.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;registry&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;DEVREGISTRY_ID&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;devregistry_new&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;registry&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;SERIAL_ID&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;registry&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The device registry is provided via the aux vector, and we can use helios::service to look it up by its interface ID. Then we use the devregistry::new operation to create a serial device:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# Device driver registry.
interface devregistry {
	# Creates a new device implementing the given interface ID using the
	# provided endpoint capability and returns its assigned serial number.
	call new{; out}(iface: u64) uint;
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After this we can destroy the registry – we won’t need it again and it’s best to get rid of it so that we can work with the minimum possible privileges at runtime. After this we initialize the serial port, acknowledge any interrupts that might have been pending before we got started, an enter the main loop.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;com_init&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq_ack&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;IRQ3&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq_ack&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;IRQ4&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;pollcap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;pollcap&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IRQ&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pollflags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RECV&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;pollcap&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;serial0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pollflags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RECV&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;revents&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pollflags&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;RECV&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dispatch_irq&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;revents&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pollflags&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;RECV&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dispatch_serial&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The dispatch_serial function is of interest, as this provides the implementation of the serial object we just created with the device registry.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;comport&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;wbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;dispatch_serial&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;serial_dispatch&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;serial_impl&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_iface&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_read&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_write&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_seek&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;get_baud&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_get_baud&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;get_charlen&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_get_charlen&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;get_stopbits&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_get_stopbits&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;get_parity&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_get_parity&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;set_baud&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_set_baud&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;set_charlen&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_set_charlen&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;set_stopbits&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_set_stopbits&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;set_parity&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_set_parity&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serial_read&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; size &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;PAGEMASK&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;map_flags&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;W&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rconsume&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages_static&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;store_reply&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;CADDR_UNDEF&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages_static&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; 0&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// (other functions omitted)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll skip much of the implementation details for this specific driver, but I’ll show you how read works at least. It’s relatively straightforward: first we mmap the buffer provided by the caller. If there’s already readable data pending from the serial port (stored in that rpending slice in the comport struct, which is a slice of the statically-allocated rbuf field), we copy it into the buffer and return the number of bytes we had ready. Otherwise, we stash details about the caller, storing the special reply capability in our cspace (this is one of the reasons we need cspace = self in our manifest) so we can reply to this call once data is available. Then we return to the main loop.&lt;/p&gt;&lt;p&gt;The main loop also wakes up on an interrupt, and we have an interrupt unmasked on the serial device to wake us whenever there’s data ready to be read. Eventually this gets us here, which finishes the call we saved earlier:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Reads data from the serial port&amp;apos;s RX FIFO.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;com_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;LSR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;RBF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;RBF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;RBR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// If the buffer is full we just drop chars&lt;/span&gt;
			&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;rconsume&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope that gives you a general idea of how drivers work in this environment! I encourage you to read the full implementation if you’re curious to know more about the serial driver in particular – it’s just 370 lines of code.&lt;/p&gt;&lt;p&gt;The last thing I want to show you is how the driver gets executed in the first place. When Helios boots up, it starts /sbin/sysinit, which is provided by Mercury and offers various low-level userspace runtime services, such as the device registry and bootstrap filesystem we saw earlier. After setting up its services, sysinit executes /sbin/usrinit, which is provided by the next layer up (Gaia, eventually) and sets up the rest of the system according to user policy, mounting filesystems and starting up drivers and such. At the moment, usrinit is fairly simple, and just runs a little demo. Here it is in full:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fs&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FS_ID&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;procmgr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PROCMGR_ID&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;devmgr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;DEVMGR_ID&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;devload&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;DEVLOADER_ID&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;[usrinit] Running /sbin/drv/serial&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;/sbin/drv/serial&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;procmgr_new&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;procmgr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;devloader_load&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;devload&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;process_start&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;[usrinit] open device serial0&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;devmgr_open&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;devmgr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SERIAL_ID&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;comment spell&quot;&gt;// CR =&amp;gt; LF&lt;/span&gt;
		&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;character string_escape&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;character string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;comment spell&quot;&gt;// echo&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each of the services shown at the start are automatically provided in usrinit’s aux vector by sysinit, and includes all of the services required to bootstrap the system. This includes a filesystem (the initramfs), a process manager (to start up new processes), the device manager, and the driver loader service.&lt;/p&gt;&lt;p&gt;usrinit starts by opening up /sbin/drv/serial (the serial driver, of course) from the provided initramfs using fs::open, which is a convenience wrapper around the filesystem protocol. Then we create a new process with the process manager, which by default has an empty address space – we could load a normal process into it with sys::process_load, but we want to load a driver, so we use the devloader interface instead. Then we start the process and boom: the serial driver is online.&lt;/p&gt;&lt;p&gt;The serial driver registers itself with the device registry, which means that we can use the device manager to open the 0th device which implements the serial interface. Since this is compatible with the io::file interface, it can simply be used normally with io::read and io::write to utilize the serial port. The main loop simply echos data read from the serial port back out. Simple!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;That’s a quick introduction to the driver environment provided by Mercury. I intend to write a few more drivers soon myself – PC keyboard, framebuffer, etc – and set up a simple shell. We have seen a few sample drivers written pre-Mercury which would be nice to bring into this environment, such as virtio networking and block devices. It will be nice to see them re-introduced in an environment where they can provide useful services to the rest of userspace.&lt;/p&gt;&lt;p&gt;If you’re interested in learning more about Helios or Mercury, consult &lt;a href=&quot;https://ares-os.org&quot; target=&quot;_blank&quot;&gt;ares-os.org&lt;/a&gt; for documentation – though beware of the many stub pages. If you have any questions or want to get involved in writing some drivers yourself, jump into our IRC channel: #helios on Libera Chat.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Drivers-and-mercury/</link>
        
        <pubDate>Sat, 08 Apr 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Drivers-and-mercury/</guid>
      </item>
    
      <item>
        
        
          <title>When to comment that code</title>
          <description>
            &lt;p&gt;My software tends to have a surprisingly low number of comments. One of my projects, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc&quot; target=&quot;_blank&quot;&gt;scdoc&lt;/a&gt;, has 25 comments among its 1,133 lines of C code, or 2%, compared to the average of 19%.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Naturally, I insist that my code is well-written in spite of this divergence from the norm. Allow me to explain.&lt;/p&gt;&lt;p&gt;The philosophy and implementation of code comments varies widely in the industry, and some view comment density as a proxy for code quality.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; I’ll state my views here, but will note that yours may differ and I find that acceptable; I am not here to suggest that your strategy is wrong and I will happily adopt it when I write a patch for your codebase.&lt;/p&gt;&lt;p&gt;Let’s begin with an illustrative example from one of my projects:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Reads the next entry from an EFI [[FILE_PROTOCOL]] handle of an open&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// directory. The return value is statically allocated and will be overwritten&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// on the next call.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;readdir&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_PROTOCOL&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_INFO&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// size(FILE_INFO) plus reserve up to 512 bytes for file name (FAT32&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// maximum, times two for wstr encoding)&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_INFO_SIZE&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_INFO&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code illustrates two of my various approaches to writing comments. The first comment is a documentation comment: the intended audience is the consumer of this API. The call-site has access to the following information:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;This comment&lt;/li&gt;&lt;li&gt;The name of the function, and the module in which it resides (efi::readdir)&lt;/li&gt;&lt;li&gt;The parameter names and types&lt;/li&gt;&lt;li&gt;The return type&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The goal is for the user of this function to gather enough information from these details to correctly utilize this API.&lt;/p&gt;&lt;p&gt;The module in which it resides suggests that this function interacts with the EFI (Extensible Firmware Interface) standard, and the user would be wise to pair a reading of this code (or API) with skimming the relevant standard. Indeed, the strategic naming of the FILE_PROTOCOL and FILE_INFO types (notably written in defiance of the Hare style guide), provide hints to the relevant parts of the EFI specification to read for a complete understanding of this code.&lt;/p&gt;&lt;p&gt;The name of the function is also carefully chosen to carry some weight: it is a reference to the Unix readdir function, which brings with it an intuition about its purpose and usage for programmers familiar with a Unix environment.&lt;/p&gt;&lt;p&gt;The return type also provides hints about the function’s use: it may return either a FILE_INFO pointer, void (nothing), or an error. Without reading the documentation string, and taking the name and return type into account, we might (correctly) surmise that we need to call this function repeatedly to read file details out of a directory until it returns void, indicating that all entries have been processed, handling any errors which might occur along the way.&lt;/p&gt;&lt;p&gt;We have established a lot of information about this function without actually reading the comment; in my philosophy of programming I view this information as a critical means for the author to communicate to the user, and we can lean on it to reduce the need for explicit documentation. Nevertheless, the documentation comment adds something here. The first sentence is a relatively information-sparse summary of the function’s purpose, and mainly exists to tick a box in the Hare style guide.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; The second sentence is the only real reason this comment exists: to clarify an important detail for the user which is not apparent from the function signature, namely the storage semantics associated with the return value.&lt;/p&gt;&lt;p&gt;Let’s now study the second comment’s purpose:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// size(FILE_INFO) plus reserve up to 512 bytes for file name (FAT32&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// maximum, times two for wstr encoding)&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_INFO_SIZE&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This comment exists to explain the use of the magic constant of 512. The audience of this comment is someone reading the &lt;em&gt;implementation&lt;/em&gt; of this function. This audience has access to a different context than the user of the function, for instance they are expected to have a more comprehensive knowledge of EFI and are &lt;em&gt;definitely&lt;/em&gt; expected to be reading the specification to a much greater degree of detail. We can and should lean on that context to make our comments more concise and useful.&lt;/p&gt;&lt;p&gt;An alternative writing which does not rely on this context, and which in my view is strictly worse, may look like the following:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// The FILE_INFO structure includes the file details plus a variable length&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// array for the filename. The underlying filesystem is always FAT32 per the&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// EFI specification, which has a maximum filename length of 256 characters. The&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// filename is encoded as a wide-string (UCS-2), which encodes two bytes per&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// character, and is not NUL-terminated, so we need to reserve up to 512 bytes&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// for the filename.&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FILE_INFO_SIZE&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The target audience of this comment should have a reasonable understanding of EFI. We simply need to clarify that this constant is the FAT32 max filename length, times two to account for the wstr encoding, and our magic constant is sufficiently explained.&lt;/p&gt;&lt;p&gt;Let’s move on to another kind of comment I occasionally write: medium-length prose. These often appear at the start of a function or the start of a file and serve to add context to the implementation, to justify the code’s existence or explain why it works. Another sample:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;init_pagetables&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// 0xFFFF0000xxxxxxxx - 0xFFFF0200xxxxxxxx: identity map&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// 0xFFFF0200xxxxxxxx - 0xFFFF0400xxxxxxxx: identity map (dev)&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// 0xFFFF8000xxxxxxxx - 0xFFFF8000xxxxxxxx: kernel image&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;//&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// L0[0x000]    =&amp;gt; L1_ident&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// L0[0x004]    =&amp;gt; L1_devident&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// L1_ident[*]  =&amp;gt; 1 GiB identity mappings&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// L0[0x100]    =&amp;gt; L1_kernel&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// L1_kernel[0] =&amp;gt; L2_kernel&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// L2_kernel[0] =&amp;gt; L3_kernel&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// L3_kernel[0] =&amp;gt; 4 KiB kernel pages&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;L0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0x000&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_TABLE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;L1_ident&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_AF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;L0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0x004&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_TABLE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;L1_devident&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_AF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;L0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0x100&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_TABLE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;L1_kernel&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_AF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;L1_kernel&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_TABLE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;L2_kernel&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_AF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;L2_kernel&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_TABLE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;L3_kernel&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_AF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;L1_ident&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;L1_ident&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_BLOCK&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x40000000&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;PT_NORMAL&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_AF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_ISM&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_RW&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;L1_devident&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;L1_devident&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_BLOCK&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x40000000&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;PT_DEVICE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_AF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_ISM&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_RW&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This comment shares a trait with the previous example: its purpose, in part, is to justify magic constants. It explains the indices of the arrays by way of the desired address space, and a perceptive reader will notice that 1 GiB = 1073741824 bytes = 0x40000000 bytes.&lt;/p&gt;&lt;p&gt;To fully understand this, we must again consider the intended audience. This is an implementation comment, so the reader is an &lt;em&gt;implementer&lt;/em&gt;. They will need to possess some familiarity with the behavior of page tables to be productive in this code, and they likely have the ARM manual up on their second monitor. This comment simply fills in the blanks for an informed reader.&lt;/p&gt;&lt;p&gt;There are two additional kinds of comments I often write: TODO and XXX.&lt;/p&gt;&lt;p&gt;A TODO comment indicates some important implementation deficiency; it &lt;em&gt;must&lt;/em&gt; be addressed at some point in the future and generally indicates that the function does not meet its stated interface and is often accompanied by an assertion, or a link to a ticket on the bug tracker, or both.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO: support multiple senders&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function should support multiple senders, but does not; an assertion here prevents the code from running under conditions it does not yet support and the TODO comment indicates that this should be addressed in the future. The target audience for this comment is someone who brings about these conditions and runs into the assertion failure.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;memory_empty&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;mem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// XXX: This O(n) linked list traversal is bad&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pages&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0u&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;FREELIST_END&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pages&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;phys&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;mem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;mem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;phys_tokernel&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pages&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we find an example of an XXX comment. This code is correct: it implements the function’s interface perfectly. However, given its expected usage, a performance of O(n) is not great: this function is expected to be used in hot paths. This comment documents the deficiency, and provides a hint to a reader that might be profiling this code in regards to a possible improvement.&lt;/p&gt;&lt;p&gt;One final example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Invalidates the TLB for a virtual address.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;invalidate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;virt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// TODO: Notify other cores (XXX SMP)&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;invlpg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;virt&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is an atypical usage of XXX, but one which I still occasionally reach for. Here we have a TODO comment which indicates a case which this code does not consider, but which must be addressed in the future: it will have to raise an IPI to get other cores to invalidate the affected virtual address. However, this is one of many changes which fall under a broader milestone of SMP support, and the “XXX SMP” comment is here to make it easy to grep through the codebase for any places which are known to require attention while implementing SMP support. An XXX comment is often written for the purpose of being easily found with grep.&lt;/p&gt;&lt;p&gt;That sums up most of the common reasons I will write a comment in my software. Each comment is written considering a target audience and the context provided by the code in which it resides, and aims to avoid stating redundant information within these conditions. It’s for this reason that my code is sparse on comments: I find the information outside of the comments equally important and aim to be concise such that a comment is not redundant with information found elsewhere.&lt;/p&gt;&lt;p&gt;Hopefully this post inspired some thought in you, to consider your comments deliberately and to be more aware of your ability to communicate information in other ways. Even if you chose to write your comments more densely than I do, I hope you will take care to communicate well through other mediums in your code as well.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Comment-or-no-comment/</link>
        
        <pubDate>Thu, 09 Mar 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Comment-or-no-comment/</guid>
      </item>
    
      <item>
        
        
          <title>Porting Helios to aarch64 for my FOSDEM talk, part one</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Helios&lt;/a&gt; is a microkernel written in the &lt;a href=&quot;https://harelang.org&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt; programming language, and the subject of a talk I did at FOSDEM earlier this month. You can watch the talk here if you like:&lt;/p&gt;&lt;p&gt;&lt;video controls&gt;
&lt;source src=&quot;https://video.fosdem.org/2023/H.2215%20(Ferrer)/helios.webm&quot;&gt;
&lt;/video&gt;&lt;/p&gt;&lt;p&gt;A while ago I promised someone that I would not do any talks on Helios until I could present them from Helios itself, and at FOSDEM I made good on that promise: my talk was presented from a Raspberry Pi 4 running Helios. The kernel was originally designed for x86_64 (though we were careful to avoid painting ourselves into any corners so that we could port it to more architectures later on), and I initially planned to write an Intel HD Graphics driver so that I could drive the projector from my laptop. But, after a few days spent trying to comprehend the IHD manuals, I decided it would be &lt;em&gt;much&lt;/em&gt; easier to port the entire system to aarch64 and write a driver for the much-simpler RPi GPU instead. 42 days later the port was complete, and a week or so after that I successfully presented the talk at FOSDEM. In a series of blog posts, I will take a look at those 42 days of work and explain how the aarch64 port works. Today’s post focuses on the bootloader.&lt;/p&gt;&lt;p&gt;The Helios boot-up process is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Bootloader starts up and loads the kernel, then jumps to it&lt;/li&gt;&lt;li&gt;The kernel configures the system and loads the init process&lt;/li&gt;&lt;li&gt;Kernel provides runtime services to init (and any subsequent processes)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In theory, the port to aarch64 would address these steps in order, but in practice step (2) relies heavily on the runtime services provided by step (3), so much of the work was ordered 1, 3, 2. This blog post focuses on part 1, I’ll cover parts 2 and 3 and all of the fun problems they caused in later posts.&lt;/p&gt;&lt;p&gt;In any case, the bootloader was the first step. Some basic changes to the build system established boot/+aarch64 as the aarch64 bootloader, and a simple qemu-specific ARM kernel was prepared which just gave a little “hello world” to demonstrate the multi-arch build system was working as intended. More build system refinements would come later, but it’s off to the races from here. Targeting qemu’s aarch64 virt platform was useful for most of the initial debugging and bring-up (and is generally useful at all times, as a much easier platform to debug than real hardware); the first tests on real hardware came much later.&lt;/p&gt;&lt;p&gt;Booting up is a sore point on most systems. It involves a lot of arch-specific procedures, but also generally calls for custom binary formats and annoying things like disk drivers — which don’t belong in a microkernel. So the Helios bootloaders are separated from the kernel proper, which is a simple ELF executable. The bootloader loads this ELF file into memory, configures a few simple things, then passes some information along to the kernel entry point. The bootloader’s memory and other resources are hereafter abandoned and are later reclaimed for general use.&lt;/p&gt;&lt;p&gt;On aarch64 the boot story is pretty abysmal, and I wanted to avoid adding the SoC-specific complexity which is endemic to the platform. Thus, two solutions are called for: &lt;a href=&quot;https://uefi.org/specifications&quot; target=&quot;_blank&quot;&gt;EFI&lt;/a&gt; and &lt;a href=&quot;https://www.devicetree.org/specifications/&quot; target=&quot;_blank&quot;&gt;device trees&lt;/a&gt;. At the bootloader level, EFI is the more important concern. For qemu-virt and Raspberry Pi, &lt;a href=&quot;https://github.com/tianocore/edk2&quot; target=&quot;_blank&quot;&gt;edk2&lt;/a&gt; is the free-software implementation of choice when it comes to EFI. The first order of business is producing an executable which can be loaded by EFI, which is, rather unfortunately, based on the Windows &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/debug/pe-format&quot; target=&quot;_blank&quot;&gt;COFF/PE32+&lt;/a&gt; format. I took inspiration from Linux and made an disgusting EFI stub solution, which involves hand-writing a PE32+ header in assembly and doing some truly horrifying things with binutils to massage everything into order. Much of the header is lifted from Linux:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;.section .text.head
.global base
base:
.L_head:
	/* DOS header */
	.ascii &amp;quot;MZ&amp;quot;
	.skip 58
	.short .Lpe_header - .L_head
	.align 4
.Lpe_header:
	.ascii &amp;quot;PE\0\0&amp;quot;
	.short 0xAA64                              /* Machine = AARCH64 */
	.short 2                                   /* NumberOfSections */
	.long 0                                    /* TimeDateStamp */
	.long 0                                    /* PointerToSymbolTable */
	.long 0                                    /* NumberOfSymbols */
	.short .Lsection_table - .Loptional_header /* SizeOfOptionalHeader */
	/* Characteristics:
	 * IMAGE_FILE_EXECUTABLE_IMAGE |
	 * IMAGE_FILE_LINE_NUMS_STRIPPED |
	 * IMAGE_FILE_DEBUG_STRIPPED */
	.short 0x206
.Loptional_header:
	.short 0x20b                     /* Magic = PE32+ (64-bit) */
	.byte 0x02                       /* MajorLinkerVersion */
	.byte 0x14                       /* MinorLinkerVersion */
	.long _data - .Lefi_header_end   /* SizeOfCode */
	.long __pecoff_data_size         /* SizeOfInitializedData */
	.long 0                          /* SizeOfUninitializedData */
	.long _start - .L_head           /* AddressOfEntryPoint */
	.long .Lefi_header_end - .L_head /* BaseOfCode */
.Lextra_header:
	.quad 0                          /* ImageBase */
	.long 4096                       /* SectionAlignment */
	.long 512                        /* FileAlignment */
	.short 0                         /* MajorOperatingSystemVersion */
	.short 0                         /* MinorOperatingSystemVersion */
	.short 0                         /* MajorImageVersion */
	.short 0                         /* MinorImageVersion */
	.short 0                         /* MajorSubsystemVersion */
	.short 0                         /* MinorSubsystemVersion */
	.long 0                          /* Reserved */

	.long _end - .L_head             /* SizeOfImage */

	.long .Lefi_header_end - .L_head /* SizeOfHeaders */
	.long 0                          /* CheckSum */
	.short 10                        /* Subsystem = EFI application */
	.short 0                         /* DLLCharacteristics */
	.quad 0                          /* SizeOfStackReserve */
	.quad 0                          /* SizeOfStackCommit */
	.quad 0                          /* SizeOfHeapReserve */
	.quad 0                          /* SizeOfHeapCommit */
	.long 0                          /* LoaderFlags */
	.long 6                          /* NumberOfRvaAndSizes */

	.quad 0 /* Export table */
	.quad 0 /* Import table */
	.quad 0 /* Resource table */
	.quad 0 /* Exception table */
	.quad 0 /* Certificate table */
	.quad 0 /* Base relocation table */

.Lsection_table:
	.ascii &amp;quot;.text\0\0\0&amp;quot;              /* Name */
	.long _etext - .Lefi_header_end   /* VirtualSize */
	.long .Lefi_header_end - .L_head  /* VirtualAddress */
	.long _etext - .Lefi_header_end   /* SizeOfRawData */
	.long .Lefi_header_end - .L_head  /* PointerToRawData */
	.long 0                           /* PointerToRelocations */
	.long 0                           /* PointerToLinenumbers */
	.short 0                          /* NumberOfRelocations */
	.short 0                          /* NumberOfLinenumbers */
	/* IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE */
	.long 0x60000020

	.ascii &amp;quot;.data\0\0\0&amp;quot;        /* Name */
	.long __pecoff_data_size    /* VirtualSize */
	.long _data - .L_head       /* VirtualAddress */
	.long __pecoff_data_rawsize /* SizeOfRawData */
	.long _data - .L_head       /* PointerToRawData */
	.long 0                     /* PointerToRelocations */
	.long 0                     /* PointerToLinenumbers */
	.short 0                    /* NumberOfRelocations */
	.short 0                    /* NumberOfLinenumbers */
	/* IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE */
	.long 0xc0000040

.balign 0x10000
.Lefi_header_end:

.global _start
_start:
	stp x0, x1, [sp, -16]!

	adrp x0, base
	add x0, x0, #:lo12:base
	adrp x1, _DYNAMIC
	add x1, x1, #:lo12:_DYNAMIC
	bl relocate
	cmp w0, #0
	bne 0f

	ldp x0, x1, [sp], 16

	b bmain

0:
	/* relocation failed */
	add sp, sp, -16
	ret
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The specific details about how any of this works are complex and unpleasant, I’ll refer you to the spec if you’re curious, and offer a general suggestion that cargo-culting my work here would be a lot easier than understanding it should you need to build something similar.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Note the entry point for later; we store two arguments from EFI (x0 and x1) on the stack and eventually branch to bmain.&lt;/p&gt;&lt;p&gt;This file is assisted by the linker script:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ENTRY(_start)
OUTPUT_FORMAT(elf64-littleaarch64)

SECTIONS {
	/DISCARD/ : {
		*(.rel.reloc)
		*(.eh_frame)
		*(.note.GNU-stack)
		*(.interp)
		*(.dynsym .dynstr .hash .gnu.hash)
	}

	. = 0xffff800000000000;

	.text.head : {
		_head = .;
		KEEP(*(.text.head))
	}

	.text : ALIGN(64K) {
		_text = .;
		KEEP(*(.text))
		*(.text.*)
		. = ALIGN(16);
		*(.got)
	}

	. = ALIGN(64K);
	_etext = .;

	.dynamic : {
		*(.dynamic)
	}

	.data : ALIGN(64K) {
		_data = .;
		KEEP(*(.data))
		*(.data.*)

		/* Reserve page tables */
		. = ALIGN(4K);
		L0 = .;
		. += 512 * 8;
		L1_ident = .;
		. += 512 * 8;
		L1_devident = .;
		. += 512 * 8;
		L1_kernel = .;
		. += 512 * 8;
		L2_kernel = .;
		. += 512 * 8;
		L3_kernel = .;
		. += 512 * 8;
	}

	.rela.text : {
		*(.rela.text)
		*(.rela.text*)
	}
	.rela.dyn : {
		*(.rela.dyn)
	}
	.rela.plt : {
		*(.rela.plt)
	}
	.rela.got : {
		*(.rela.got)
	}
	.rela.data : {
		*(.rela.data)
		*(.rela.data*)
	}

	.pecoff_edata_padding : {
		BYTE(0);
		. = ALIGN(512);
	}
	__pecoff_data_rawsize = ABSOLUTE(. - _data);
	_edata = .;

	.bss : ALIGN(4K) {
		KEEP(*(.bss))
		*(.bss.*)
		*(.dynbss)
	}

	. = ALIGN(64K);
	__pecoff_data_size = ABSOLUTE(. - _data);
	_end = .;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Items of note here are the careful treatment of relocation sections (cargo-culted from earlier work on RISC-V with Hare; not actually necessary as qbe generates PIC for aarch64)&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and the extra symbols used to gather information for the PE32+ header. Padding is also added in the required places, and static aarch64 page tables are defined for later use.&lt;/p&gt;&lt;p&gt;This is built as a shared object, and the Makefile &lt;del&gt;mutilates&lt;/del&gt; reformats the resulting ELF file to produce a PE32+ executable:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$(BOOT)/bootaa64.so: $(BOOT_OBJS) $(BOOT)/link.ld
	$(LD) -Bsymbolic -shared --no-undefined \
		-T $(BOOT)/link.ld \
		$(BOOT_OBJS) \
		-o $@

$(BOOT)/bootaa64.efi: $(BOOT)/bootaa64.so
	$(OBJCOPY) -Obinary \
		-j .text.head -j .text -j .dynamic -j .data \
		-j .pecoff_edata_padding \
		-j .dynstr -j .dynsym \
		-j .rel -j .rel.* -j .rel* \
		-j .rela -j .rela.* -j .rela* \
		$&amp;lt; $@
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With all of this mess sorted, and the PE32+ entry point branching to bmain, we can finally enter some Hare code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;export fn bmain(
	image_handle: efi::HANDLE,
	systab: *efi::SYSTEM_TABLE,
) efi::STATUS = {
    // ...
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Getting just this far took 3 full days of work.&lt;/p&gt;&lt;p&gt;Initially, the Hare code incorporated a lot of proof-of-concept work from Alexey Yerin’s “carrot” kernel prototype for RISC-V, which also booted via EFI. Following the early bringing-up of the bootloader environment, this was refactored into a more robust and general-purpose EFI support layer for Helios, which will be applicable to future ports. The purpose of this module is to provide an idiomatic Hare-oriented interface to the EFI boot services, which the bootloader makes use of mainly to read files from the boot media and examine the system’s memory map.&lt;/p&gt;&lt;p&gt;Let’s take a look at the first few lines of bmain:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;efi::init(image_handle, systab)!;

const eficons = eficons_init(systab);
log::setcons(&amp;amp;eficons);
log::printfln(&amp;quot;Booting Helios aarch64 via EFI&amp;quot;);

if (readel() == el::EL3) {
	log::printfln(&amp;quot;Booting from EL3 is not supported&amp;quot;);
	return efi::STATUS::LOAD_ERROR;
};

let mem = allocator { ... };
init_mmap(&amp;amp;mem);
init_pagetables();
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Significant build system overhauls were required such that Hare modules from the kernel like log (and, later, other modules like elf) could be incorporated into the bootloader, simplifying the process of implementing more complex bootloaders. The first call of note here is init_mmap, which scans the EFI memory map and prepares a simple high-watermark allocator to be used by the bootloader to allocate memory for the kernel image and other items of interest. It’s quite simple, it just finds the largest area of general-purpose memory and sets up an allocator with it:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// Loads the memory map from EFI and initializes a page allocator using the
// largest area of physical memory.
fn init_mmap(mem: *allocator) void = {
	const iter = efi::iter_mmap()!;
	let maxphys: uintptr = 0, maxpages = 0u64;
	for (true) {
		const desc = match (efi::mmap_next(&amp;amp;iter)) {
		case let desc: *efi::MEMORY_DESCRIPTOR =&amp;gt;
			yield desc;
		case void =&amp;gt;
			break;
		};
		if (desc.DescriptorType != efi::MEMORY_TYPE::CONVENTIONAL) {
			continue;
		};
		if (desc.NumberOfPages &amp;gt; maxpages) {
			maxphys = desc.PhysicalStart;
			maxpages = desc.NumberOfPages;
		};
	};
	assert(maxphys != 0, &amp;quot;No suitable memory area found for kernel loader&amp;quot;);
	assert(maxpages &amp;lt;= types::UINT_MAX);
	pagealloc_init(mem, maxphys, maxpages: uint);
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;init_pagetables is next. This populates the page tables reserved by the linker with the desired higher-half memory map, illustrated in the comments shown here:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;fn init_pagetables() void = {
	// 0xFFFF0000xxxxxxxx - 0xFFFF0200xxxxxxxx: identity map
	// 0xFFFF0200xxxxxxxx - 0xFFFF0400xxxxxxxx: identity map (dev)
	// 0xFFFF8000xxxxxxxx - 0xFFFF8000xxxxxxxx: kernel image
	//
	// L0[0x000]    =&amp;gt; L1_ident
	// L0[0x004]    =&amp;gt; L1_devident
	// L1_ident[*]  =&amp;gt; 1 GiB identity mappings
	// L0[0x100]    =&amp;gt; L1_kernel
	// L1_kernel[0] =&amp;gt; L2_kernel
	// L2_kernel[0] =&amp;gt; L3_kernel
	// L3_kernel[0] =&amp;gt; 4 KiB kernel pages
	L0[0x000] = PT_TABLE | &amp;amp;L1_ident: uintptr | PT_AF;
	L0[0x004] = PT_TABLE | &amp;amp;L1_devident: uintptr | PT_AF;
	L0[0x100] = PT_TABLE | &amp;amp;L1_kernel: uintptr | PT_AF;
	L1_kernel[0] = PT_TABLE | &amp;amp;L2_kernel: uintptr | PT_AF;
	L2_kernel[0] = PT_TABLE | &amp;amp;L3_kernel: uintptr | PT_AF;
	for (let i = 0u64; i &amp;lt; len(L1_ident): u64; i += 1) {
		L1_ident[i] = PT_BLOCK | (i * 0x40000000): uintptr |
			PT_NORMAL | PT_AF | PT_ISM | PT_RW;
	};
	for (let i = 0u64; i &amp;lt; len(L1_devident): u64; i += 1) {
		L1_devident[i] = PT_BLOCK | (i * 0x40000000): uintptr |
			PT_DEVICE | PT_AF | PT_ISM | PT_RW;
	};
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In short, we want three larger memory regions to be available: an identity map, where physical memory addresses correlate 1:1 with virtual memory, an identity map configured for device MMIO (e.g. with caching disabled), and an area to load the kernel image. The first two are straightforward, they use uniform 1 GiB mappings to populate their respective page tables. The latter is slightly more complex, ultimately the kernel is loaded in 4 KiB pages so we need to set up intermediate page tables for that purpose.&lt;/p&gt;&lt;p&gt;We cannot actually enable these page tables until we’re finished making use of the EFI boot services — the EFI specification requires us to preserve the online memory map at this stage of affairs. However, this does lay the groundwork for the kernel loader: we have an allocator to provide pages of memory, and page tables to set up virtual memory mappings that can be activated once we’re done with EFI. bmain thus proceeds with loading the kernel:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;const kernel = match (efi::open(&amp;quot;\\helios&amp;quot;, efi::FILE_MODE::READ)) {
case let file: *efi::FILE_PROTOCOL =&amp;gt;
	yield file;
case let err: efi::error =&amp;gt;
	log::printfln(&amp;quot;Error: no kernel found at /helios&amp;quot;);
	return err: efi::STATUS;
};

log::printfln(&amp;quot;Load kernel /helios&amp;quot;);
const kentry = match (load(&amp;amp;mem, kernel)) {
case let err: efi::error =&amp;gt;
	return err: efi::STATUS;
case let entry: uintptr =&amp;gt;
	yield entry: *kentry;
};
efi::close(kernel)!;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The loader itself (the “load” function here) is a relatively straightforward ELF loader; if you’ve seen one you’ve seen them all. Nevertheless, you may browse it &lt;a href=&quot;https://git.sr.ht/~sircmpwn/helios/tree/02d0490487c7a0fb4b0367b95819e808b98f87fb/item/boot/%2Baarch64/loader.ha&quot; target=&quot;_blank&quot;&gt;online&lt;/a&gt; if you so wish. The only item of note here is the function used for mapping kernel pages:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// Maps a physical page into the kernel&amp;apos;s virtual address space.
fn kmmap(virt: uintptr, phys: uintptr, flags: uintptr) void = {
	assert(virt &amp;amp; ~0x1ff000 == 0xffff800000000000: uintptr);
	const offs = (virt &amp;gt;&amp;gt; 12) &amp;amp; 0x1ff;
	L3_kernel[offs] = PT_PAGE | PT_NORMAL | PT_AF | PT_ISM | phys | flags;
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The assertion enforces a constraint which is implemented by our kernel linker script, namely that all loadable kernel program headers are located within the kernel’s reserved address space. With this constraint in place, the implementation is simpler than many mmap implementations; we can assume that L3_kernel is the correct page table and just load it up with the desired physical address and mapping flags.&lt;/p&gt;&lt;p&gt;Following the kernel loader, the bootloader addresses other items of interest, such as loading the device tree and boot modules — which includes, for instance, the init process image and an initramfs. It also allocates &amp; populates data structures with information which will be of later use to the kernel, including the memory map. This code is relatively straightforward and not particularly interesting; most of these processes takes advantage of the same straightforward Hare function:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// Loads a file into continuous pages of memory and returns its physical
// address.
fn load_file(
	mem: *allocator,
	file: *efi::FILE_PROTOCOL,
) (uintptr | efi::error) = {
	const info = efi::file_info(file)?;
	const fsize = info.FileSize: size;
	let npage = fsize / PAGESIZE;
	if (fsize % PAGESIZE != 0) {
		npage += 1;
	};

	let base: uintptr = 0;
	for (let i = 0z; i &amp;lt; npage; i += 1) {
		const phys = pagealloc(mem);
		if (base == 0) {
			base = phys;
		};

		const nbyte = if ((i + 1) * PAGESIZE &amp;gt; fsize) {
			yield fsize % PAGESIZE;
		} else {
			yield PAGESIZE;
		};
		let dest = (phys: *[*]u8)[..nbyte];
		const n = efi::read(file, dest)?;
		assert(n == nbyte);
	};

	return base;
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is not necessary to map these into virtual memory anywhere, the kernel later uses the identity-mapped physical memory region in the higher half to read them. Tasks of interest resume at the end of bmain:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;efi::exit_boot_services();
init_mmu();
enter_kernel(kentry, ctx);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once we exit boot services, we are free to configure the MMU according to our desired specifications and make good use of all of the work done earlier to prepare a kernel memory map. Thus, init_mmu:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// Initializes the ARM MMU to our desired specifications. This should take place
// *after* EFI boot services have exited because we&amp;apos;re going to mess up the MMU
// configuration that it depends on.
fn init_mmu() void = {
	// Disable MMU
	const sctlr_el1 = rdsctlr_el1();
	wrsctlr_el1(sctlr_el1 &amp;amp; ~SCTLR_EL1_M);

	// Configure MAIR
	const mair: u64 =
		(0xFF &amp;lt;&amp;lt; 0) | // Attr0: Normal memory; IWBWA, OWBWA, NTR
		(0x00 &amp;lt;&amp;lt; 8);  // Attr1: Device memory; nGnRnE, OSH
	wrmair_el1(mair);

	const tsz: u64 = 64 - 48;
	const ips = rdtcr_el1() &amp;amp; TCR_EL1_IPS_MASK;
	const tcr_el1: u64 =
		TCR_EL1_IPS_42B_4T |	// 4 TiB IPS
		TCR_EL1_TG1_4K |	// Higher half: 4K granule size
		TCR_EL1_SH1_IS |	// Higher half: inner shareable
		TCR_EL1_ORGN1_WB |	// Higher half: outer write-back
		TCR_EL1_IRGN1_WB |	// Higher half: inner write-back
		(tsz &amp;lt;&amp;lt; TCR_EL1_T1SZ) |	// Higher half: 48 bits
		TCR_EL1_TG0_4K |	// Lower half: 4K granule size
		TCR_EL1_SH0_IS |	// Lower half: inner sharable
		TCR_EL1_ORGN0_WB |	// Lower half: outer write-back
		TCR_EL1_IRGN0_WB |	// Lower half: inner write-back
		(tsz &amp;lt;&amp;lt; TCR_EL1_T0SZ);	// Lower half: 48 bits
	wrtcr_el1(tcr_el1);

	// Load page tables
	wrttbr0_el1(&amp;amp;L0[0]: uintptr);
	wrttbr1_el1(&amp;amp;L0[0]: uintptr);
	invlall();

	// Enable MMU
	const sctlr_el1: u64 =
		SCTLR_EL1_M |		// Enable MMU
		SCTLR_EL1_C |		// Enable cache
		SCTLR_EL1_I |		// Enable instruction cache
		SCTLR_EL1_SPAN |	// SPAN?
		SCTLR_EL1_NTLSMD |	// NTLSMD?
		SCTLR_EL1_LSMAOE |	// LSMAOE?
		SCTLR_EL1_TSCXT |	// TSCXT?
		SCTLR_EL1_ITD;		// ITD?
	wrsctlr_el1(sctlr_el1);
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are a lot of bits here! Figuring out which ones to enable or disable was a project in and of itself. One of the major challenges, funnily enough, was finding the correct ARM manual to reference to understand all of these registers. I’ll save you some time and &lt;a href=&quot;https://redacted.moe/f/f0255890.pdf&quot; target=&quot;_blank&quot;&gt;link to it&lt;/a&gt; directly, should you ever find yourself writing similar code. Some question marks in comments towards the end point out some flags that I’m still not sure about. The ARM CPU is &lt;em&gt;very&lt;/em&gt; configurable and identifying the configuration that produces the desired behavior for a general-purpose kernel requires some effort.&lt;/p&gt;&lt;p&gt;After this function completes, the MMU is initialized and we are up and running with the kernel memory map we prepared earlier; the kernel is loaded in the higher half and the MMU is prepared to service it. So, we can jump to the kernel via enter_kernel:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;@noreturn fn enter_kernel(entry: *kentry, ctx: *bootctx) void = {
	const el = readel();
	switch (el) {
	case el::EL0 =&amp;gt;
		abort(&amp;quot;Bootloader running in EL0, breaks EFI invariant&amp;quot;);
	case el::EL1 =&amp;gt;
		// Can boot immediately
		entry(ctx);
	case el::EL2 =&amp;gt;
		// Boot from EL2 =&amp;gt; EL1
		//
		// This is the bare minimum necessary to get to EL1. Future
		// improvements might be called for here if anyone wants to
		// implement hardware virtualization on aarch64. Good luck to
		// this future hacker.

		// Enable EL1 access to the physical counter register
		const cnt = rdcnthctl_el2();
		wrcnthctl_el2(cnt | 0b11);

		// Enable aarch64 in EL1 &amp;amp; SWIO, disable most other EL2 things
		// Note: I bet someday I&amp;apos;ll return to this line because of
		// Problems
		const hcr: u64 = (1 &amp;lt;&amp;lt; 1) | (1 &amp;lt;&amp;lt; 31);
		wrhcr_el2(hcr);

		// Set up SPSR for EL1
		// XXX: Magic constant I have not bothered to understand
		wrspsr_el2(0x3c4);

		enter_el1(ctx, entry);
	case el::EL3 =&amp;gt;
		// Not supported, tested earlier on
		abort(&amp;quot;Unsupported boot configuration&amp;quot;);
	};
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we see the detritus from one of many battles I fought to port this kernel: the EL2 =&gt; EL1 transition. aarch64 has several “exception levels”, which are semantically similar to the x86_64 concept of protection rings. EL0 is used for userspace code, which is not applicable under these circumstances; an assertion sanity-checks this invariant. EL1 is the simplest case, this is used for normal kernel code and in this situation we can jump directly to the kernel. The EL2 case is used for hypervisor code, and this presented me with a challenge. When I tested my bootloader in qemu-virt, it worked initially, but on real hardware it failed. After much wailing and gnashing of teeth, the cause was found to be that our bootloader was started in EL2 on real hardware, and EL1 on qemu-virt. qemu can be configured to boot in EL2, which was crucial in debugging this problem, via -M virt,virtualization=on. From this environment I was able to identify a few important steps to drop to EL1 and into the kernel, though from the comments you can probably ascertain that this process was not well-understood. I do have a better understanding of it now than I did when this code was written, but the code is still serviceable and I see no reason to change it at this stage.&lt;/p&gt;&lt;p&gt;At this point, 14 days into the port, I successfully reached kmain on qemu-virt. Some initial kernel porting work was done after this, but when I was prepared to test it on real hardware I ran into this EL2 problem — the first kmain on real hardware ran at T+18.&lt;/p&gt;&lt;p&gt;That sums it up for the aarch64 EFI bootloader work. 24 days later the kernel and userspace ports would be complete, and a couple of weeks after that it was running on stage at FOSDEM. The next post will cover the kernel port (maybe more than one post will be required, we’ll see), and the final post will address the userspace port and the inner workings of the slidedeck demo that was shown on stage. Look forward to it, and thanks for reading!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Helios-aarch64/</link>
        
        <pubDate>Mon, 20 Feb 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Helios-aarch64/</guid>
      </item>
    
      <item>
        
        
          <title>Should private platforms engage in censorship?</title>
          <description>
            &lt;p&gt;Private service providers are entitled to do business with whom they please, or not to. Occasionally, a platform will take advantage of this to deny service to a particular entity on any number of grounds, often igniting a flood of debate online regarding whether or not censorship in this form is just. Recently, CloudFlare pulled the plug on a certain forum devoted to the coordinated harassment of its victims. Earlier examples include the same service blocking a far-right imageboard, or Namecheap cancelling service for a neo-Nazi news site.&lt;/p&gt;&lt;p&gt;In each of these cases, a private company elected to terminate service for a customer voluntarily, without a court order. Absent from these events was any democratic or judicial oversight. A private company which provides some kind of infrastructure for the Internet simply elected to unilaterally terminate service for a customer or class of customers.&lt;/p&gt;&lt;p&gt;When private companies choose with whom they do or do not do business with, this is an exercise of an important freedom: &lt;a href=&quot;https://en.wikipedia.org/wiki/Freedom_of_association&quot; target=&quot;_blank&quot;&gt;freedom of association&lt;/a&gt;. Some companies have this right limited by regulation — for instance, utility companies are often required to provide power to everyone who wants it within their service area. Public entities are required to provide their services to everyone — for instance, the US postal service cannot unilaterally choose not to deliver your mail. However, by default, private companies are generally allowed to deny their services to whomever they please.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Are they right to?&lt;/p&gt;&lt;p&gt;An argument is often made that, when a platform reaches a given size (e.g. Facebook), or takes on certain ambitions (e.g. CloudFlare), it may become large and entrenched enough in our society that it should self-impose a role more analogous to a public utility than a private company. Under such constraints, such a platform would choose to host any content which is not explicitly illegal, and defer questions over what content is appropriate to the democratic process. There are a number of angles from which we can examine this argument.&lt;/p&gt;&lt;p&gt;For a start, how might we implement the scenario called for by this argument? Consider one option: regulation. Power companies are subject to regulations regarding how and with whom they do business; they must provide service to everyone and they are not generally allowed to shut off your heat in the cold depths of winter. Similarly, we could regulate digital platforms to require them to provide a soapbox for all legally expressible viewpoints, then utilize the democratic process to narrow this soapbox per society’s mutually-agreed-upon views regarding matters such as neo-Nazi propaganda.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;It’s important when making this argument to note that regulation of this sort imposes obligations on private businesses which erode their own right to free association; radical free speech for individuals requires radical curtailing of free association for businesses. Private businesses are owned and staffed by individuals, and requiring them to allow all legal forms of content on their platform is itself a limitation on their freedom. The staff of a newspaper may not appreciate being required by law to provide space in the editorials for KKK members to espouse their racist philosophy, but would nevertheless be required to typeset such articles under such an arrangement.&lt;/p&gt;&lt;p&gt;Another approach to addressing this argument is not to question the rights of a private business, but instead to question whether or not they should be allowed to grow to a size such that their discretion in censorship constitutes a disruption to society due to their scale and entrenched market position. Under this lens, we can suggest another government intervention that does not take the form of regulation, but of an application of antitrust law. With more platforms to choose from, we can explore more approaches to moderation and censorship, and depend on the market’s invisible hand to lead us true.&lt;/p&gt;&lt;p&gt;The free speech absolutist who makes similar arguments may find themselves in a contradiction: expanding free speech for some people (platform users) requires, in this scenario, curtailing freedoms for others (platform owners and staff). Someone in this position may concede that, while they support the rights of individuals, they might not offer the same rights to businesses who resemble utilities. The tools for implementing this worldview, however, introduce further contradictions when combined with the broader political profile of a typical free speech absolutist: calling for regulation isn’t very consistent with any “small government” philosophy; and those who describe themselves as Libertarian and make either of these arguments provide me with no small amount of amusement.&lt;/p&gt;&lt;p&gt;There is another flaw in this line of thinking which I want to highlight: the presumption that the democratic process can address these problems in the first place. Much of the legitimacy of this argument rests on the assumption that the ability for maligned users to litigate their grievances is not only more just, but also equal to the threat posed by hate speech and other concerns which are often the target of censorship on private platforms. I don’t think that this is true.&lt;/p&gt;&lt;p&gt;The democratic and judicial processes are often corrupt and inefficient. It is still the case that the tone of your skin has an outsized effect on the outcome of your court case; why shouldn’t similar patterns emerge when de-platformed racists are given their day before a judge? Furthermore, the pace of government interventions are generally insufficient. Could Facebook appeal a court for the right to remove the Proud Boys from their platform faster than they could organize an attack on the US Capitol building? And can lawmakers keep up with innovation at a pace sufficient to address new forms and mediums for communicating harmful content before they’re a problem?&lt;/p&gt;&lt;p&gt;We should also question if the democratic process will lead to moral outcomes. Minorities are, by definition, in the minority, and a purely democratic process will only favor their needs subject to the will of the majority. Should the rights of trans people to live free of harassment be subject to the pleasure of the cisgendered majority?&lt;/p&gt;&lt;p&gt;These systems, when implemented, will perform as they always have: they will provide disproportionately unfavorable outcomes for disadvantaged members of society. I am a leftist: if asked to imagine a political system which addresses these problems, I will first imagine sweeping reforms to our existing system, point out that the free market isn’t, lean in favor of regulation and nationalization of important industries, and seek to empower the powerless against the powerful. It will require a lot of difficult, ongoing work to get there, and I imagine most of this work will be done in spite of the protests of the typical free speech absolutist.&lt;/p&gt;&lt;p&gt;I am in favor of these reforms, but they are decades away from completion, and many will disagree on the goals and their implementation. But I am also a pragmatic person, and when faced with the system in which we find ourselves today, I seek a pragmatic solution to this problem; ideally one which is not predicated on revolution. When faced with the question, “should private platforms engage in censorship?”, what is the pragmatic answer?&lt;/p&gt;&lt;p&gt;To provide such an answer, we must de-emphasize idealism in favor of an honest examination of the practical context within which our decision-making is done. Consider again the status quo: private companies are generally permitted to exercise their right to free association by kicking people off of their platforms. A pragmatic framework for making these decisions examines the context in which they are made. In the current political climate, this context should consider the threats faced by many different groups of marginalized people today: racism is still alive and strong, what few LGBT rights exist are being dismantled, and many other civil liberties are under attack.&lt;/p&gt;&lt;p&gt;When someone (or some entity such as business) enjoys a particular freedom, the way they exercise it is meaningful. Inaction is a form of complicity; allowing hate to remain on your platform is an acknowledgement of your favor towards the lofty principles outlined in the arguments above &lt;em&gt;in spite of&lt;/em&gt; the problems enumerated here and the realities faced by marginalized people today. A purely moral consideration thus suggests that exercising your right to free association in your role as a decision-maker at a business is a just response to this status quo.&lt;/p&gt;&lt;p&gt;I expect the people around me (given a definition of “around me” that extends to the staff at businesses I patronize) to possess a moral compass which is compatible with my own, and to act in accordance with it; in the absence of this I will express my discontent by voting with my feet. However, businesses in the current liberal economic regime often disregard morals in favor of profit-oriented decision making. Therefore, in order for the typical business behave morally, their decision-making must exist within a context where the moral outcomes align with the profitable outcomes.&lt;/p&gt;&lt;p&gt;We are seeing increasing applications of private censorship because this alignment is present. Businesses depend on two economic factors which are related to this issue: access to a pool of profitable users, and access to a labor pool with which to develop and maintain their profits. Businesses which platform bigots are increasingly finding public opinion turning against them; marginalized people and moderates tend to flee to less toxic spaces and staff members are looking to greener pastures. The free market currently rewards private censorship, therefore in a system wherein the free market reigns supreme we observe private censorship.&lt;/p&gt;&lt;p&gt;I reject the idea that it is appropriate for businesses to sideline morality in favor of profit, and I don’t have much faith in the free market to produce moral outcomes. For example, the market is responding poorly to the threat of climate change. However, in the case of private censorship, the incentives are aligned such that the outcomes we’re observing match the outcomes I would expect.&lt;/p&gt;&lt;p&gt;This is a complex topic which we have examined from many angles. In my view, freedom of association is just as important as freedom of speech, and its application to private censorship is not clearly wrong. If you view private censorship as an infringement of the principle of free speech, but agree that freedom of association is nevertheless important, we must resolve this contradiction. The democratic or judicial processes are an enticing and idealistic answer, but these are flawed processes that may not produce just outcomes. If I were to consider these tools to address this question, I’m going to present solutions from a socialist perspective which may or may not jibe with your sensibilities.&lt;/p&gt;&lt;p&gt;Nevertheless, the system as it exists today produces outcomes which approximate both rationality and justice, and I do not stand in opposition to the increased application of private censorship under the current system, flawed though it may be.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Should-private-platforms-engage-in-censorship/</link>
        
        <pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Should-private-platforms-engage-in-censorship/</guid>
      </item>
    
      <item>
        
        
          <title>My plans at FOSDEM: SourceHut, Hare, and Helios</title>
          <description>
            &lt;p&gt;FOSDEM is right around the corner, and finally in person after long years of dealing with COVID. I’ll be there again this year, and I’m looking forward to it! I have four slots on the schedule (wow! Thanks for arranging these, FOSDEM team) and I’ll be talking about several projects. There is a quick lightning talk on Saturday to introduce Helios and tease a full-length talk on Sunday, a meetup for the Hare community, and a meetup for the SourceHut community. I hope to see you there!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://fosdem.org/2023/schedule/event/helios/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;Lightning talk: Introducing Helios&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Saturday 12:00 at H.2215 (Ferrer)&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Helios is a simple microkernel written in part to demonstrate the applicability of the Hare programming language to kernels. This talk briefly explains why Helios is interesting and is a teaser for a more in-depth talk in the microkernel room tomorrow.&lt;/p&gt;&lt;p&gt;Hare is a systems programming language designed to be simple, stable, and robust. Hare uses a static type system, manual memory management, and a minimal runtime. It is well-suited to writing operating systems, system tools, compilers, networking software, and other low-level, high performance tasks. Helios uses Hare to implement a microkernel, largely inspired by seL4.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://fosdem.org/2023/schedule/event/hare_meetup/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;BoF: The Hare programming language&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Saturday 15:00 at UB2.147&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Hare is a systems programming language designed to be simple, stable, and robust. Hare uses a static type system, manual memory management, and a minimal runtime. It is well-suited to writing operating systems, system tools, compilers, networking software, and other low-level, high performance tasks.&lt;/p&gt;&lt;p&gt;At this meeting we’ll sum up the state of affairs with Hare, our plans for the future, and encourage discussions with the community. We’ll also demonstrate a few interesting Hare projects, including Helios, a micro-kernel written in Hare, and encourage each other to work on interesting projects in the Hare community.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://fosdem.org/2023/schedule/event/sourcehut/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;BoF: SourceHut meetup&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Saturday 16:00 at UB2.147&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;SourceHut is a free software forge for developing software projects, providing git and mercurial hosting, continuous integration, mailing lists, and more. We’ll be meeting here again in 2023 to discuss the platform and its community, the completion of the GraphQL rollout and the migration to the EU, and any other topics on the minds of the attendees.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://fosdem.org/2023/schedule/event/heliosuk/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;Introducing Helios&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Sunday 13:00 at H.1308 (Rolin)&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Helios is a simple microkernel written in part to demonstrate the applicability of the Hare programming language to kernels. This talk will introduce the design and rationale for Helios, address some details of its implementation, compare it with seL4, and elaborate on the broader plans for the system.&lt;/p&gt;&lt;/blockquote&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/FOSDEM/</link>
        
        <pubDate>Tue, 24 Jan 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/FOSDEM/</guid>
      </item>
    
      <item>
        
        
          <title>Setting a new focus for my blog</title>
          <description>
            &lt;p&gt;Just shy of two months ago, I published &lt;a href=&quot;https://drewdevault.com/blog/I-shall-toil-quietly/&quot;&gt;I shall toil at a reduced volume&lt;/a&gt;, which addressed the fact that I’m not getting what I want from my blog anymore, and I would be taking an indefinite break. Well, I am ready to resume my writing, albeit with a different tone and focus than before.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Well, that was fast.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;– Everyone&lt;/p&gt;&lt;p&gt;Since writing this, I have been considering what exactly the essential subject of my dissatisfaction with my writing has been. I may have found the answer: I lost sight of my goals. I got so used to writing that I would often think to myself, “I want to write a blog post!”, then dig a topic out of my backlog (which is 264 items long) and write something about it. This is not the way; much of the effort expended on writing in this manner is not spent on the subjects I care about most, or those which most urgently demand an expenditure of words.&lt;/p&gt;&lt;p&gt;The consequences of this misalignment of perspective are that my writing has often felt dull and rote. It encourages shallower takes and lends itself to the rants or unthoughtful criticisms that my writings are, unfortunately, (in)famous for. When I take an idea off of the shelf, or am struck by an idea that, in the moment, seemingly demands to be spake of, I often end up with a disappointing result when the fruit of this inspiration is published a few hours later.&lt;/p&gt;&lt;p&gt;Over the long term, these issues manifest as demerits to my reputation, and deservedly so. What’s more, when a critical tone is well-justified, the posts which utilize it are often overlooked by readers due to the normalization of this tone throughout less important posts. Take for instance my recent post on &lt;a href=&quot;https://drewdevault.com/blog/Does-Rust-belong-in-Linux/&quot;&gt;Rust in Linux&lt;/a&gt;. Though this article could have been written with greater nuance, I still find its points about the value of conservatism in software decision-making accurate and salient. However, the message is weakened riding on the coat-tails of my long history of less poignant critiques of Rust. As I resume my writing, I will have to take a more critical examination of myself and the broader context of my writing before reaching for a negative tone as a writing tool.&lt;/p&gt;&lt;p&gt;With these lessons in mind, I am seeking out stronger goals to align my writing with, in the hope that the writing is both more fulfilling for me, and more compelling for the reader. Among these goals I have identified two particularly important ones, whose themes resonate through my strongest articles throughout the years:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The applicability of software to the just advancement of society, its contextualization within the needs of the people who use it, a deep respect for these people and the software’s broader impact on the world, and the use of free software to acknowledge and fulfill these needs.&lt;/li&gt;&lt;li&gt;The principles of good software engineering, such that software built to meet these goals is reliable, secure, and comprehensible. It is in the service of this goal that I beat the drum of simplicity with a regular rhythm.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Naturally many people have important beliefs on these subjects. I simply aim to share my own perspective, and I find it rewarding when I am able to write compelling arguments which underline these goals.&lt;/p&gt;&lt;p&gt;There is another kind of blog post that I enjoy writing and plan to resume: in-depth technical analysis of my free software projects. I’m working on lots of interesting and exciting projects, and I want to talk about them more, and I think people enjoy reading about them. I just spent six weeks porting Helios to aarch64, for instance, and have an essay on the subject half-written in the back of my head. I would love to type it in and publish it.&lt;/p&gt;&lt;p&gt;So, I will resume writing, and indeed at a “reduced volume”, with a renewed focus on the message and its context, and an emphasis on serving the goals I care about the most. Hopefully I find it more rewarding to write in this manner, and you find the results more compelling to read! Stay tuned.&lt;/p&gt;&lt;p&gt;$ rm ~/sources/drewdevault.com/todo.txt&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-new-focus/</link>
        
        <pubDate>Sun, 22 Jan 2023 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-new-focus/</guid>
      </item>
    
      <item>
        
        
          <title>I shall toil at a reduced volume</title>
          <description>
            &lt;p&gt;Over the last nine years I have written 300,000 words for this blog on the topics which are important to me. I am not certain that I have much left to say.&lt;/p&gt;&lt;p&gt;I can keep revisiting these topics for years, each time adding a couple more years of wisdom and improvements to my writing skills to present my arguments more effectively. However, I am starting to feel diminishing returns from my writing. It does not seem like my words are connecting with readers anymore. And, though the returns on my work seem to be diminishing, the costs are not. Each new article spurs less discussion than the last, but provides an unwavering supply of spiteful responses.&lt;/p&gt;&lt;p&gt;Software is still the same mess it was when I started writing and working, or perhaps even worse. You can’t overcome perverse incentives. As Cantrill once &lt;a href=&quot;https://www.youtube.com/watch?t=2483&amp;v=-zRN7XLCRhc&quot; target=&quot;_blank&quot;&gt;famously noted&lt;/a&gt;, the lawnmower can’t have empathy. The truth he did not speak is that we all have some Oracle in our hearts, and the lawnmower is the size of the entire industry.&lt;/p&gt;&lt;p&gt;I have grown tired of it. I will continue my work quietly, building the things I believe in, and remaining true to my principles. I do not yet know if this is a cessation or a siesta, but I do know that I will not write again for some time. Thank you for reading, and good luck in your endeavours. I hope you found something of value in these pages.&lt;/p&gt;&lt;p&gt;Here are some of the blog posts I am most proud of, should you want to revisit them today or the next time you happen upon my website:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/04/26/Cryptocurrency-is-a-disaster.html&quot; target=&quot;_blank&quot;&gt;Cryptocurrency is an abject disaster&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2019/09/17/The-wrong-words-but-the-right-ideas.html&quot; target=&quot;_blank&quot;&gt;Don’t sacrifice the right ideas to win the right words&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2022/02/13/Framing-accessibility-in-broader-terms.html&quot; target=&quot;_blank&quot;&gt;Framing accessibility in broader terms&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2022/03/29/free-software-free-infrastructure.html&quot; target=&quot;_blank&quot;&gt;It is important for free software to use free software infrastructure&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/01/20/FOSS-is-to-surrender-your-monopoly.html&quot; target=&quot;_blank&quot;&gt;Open source means surrendering your monopoly over commercial exploitation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/12/23/Sustainable-creativity-post-copyright.html&quot; target=&quot;_blank&quot;&gt;Sustainable creativity in a world without copyright&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/03/23/Open-sourcing-video-games.html&quot; target=&quot;_blank&quot;&gt;The complete guide for open sourcing video games&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2020/11/06/Utility-vs-usability.html&quot; target=&quot;_blank&quot;&gt;Utility vs usability&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2020/05/05/We-are-complicit-in-our-employers-deeds.html&quot; target=&quot;_blank&quot;&gt;We are complicit in our employer’s deeds&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2019/01/23/Why-I-use-old-hardware.html&quot; target=&quot;_blank&quot;&gt;Why I use old hardware&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/I-shall-toil-quietly/</link>
        
        <pubDate>Thu, 01 Dec 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/I-shall-toil-quietly/</guid>
      </item>
    
      <item>
        
        
          <title>Codegen in Hare v2</title>
          <description>
            &lt;p&gt;I spoke about code generation in Hare &lt;a href=&quot;https://drewdevault.com/2022/05/14/generating-ioctls.html&quot; target=&quot;_blank&quot;&gt;back in May&lt;/a&gt; when I wrote a tool for generating ioctl numbers. I wrote another code generator over the past few weeks, and it seems like a good time to revisit the topic on my blog to showcase another approach, and the improvements we’ve made for this use-case.&lt;/p&gt;&lt;p&gt;In this case, I wanted to generate code to implement IPC (inter-process communication) interfaces for my operating system. I have designed a &lt;abbr
title=&quot;domain-specific language&quot;&gt;DSL&lt;/abbr&gt; for describing these interfaces — you can &lt;a href=&quot;https://git.sr.ht/~sircmpwn/ipcgen/tree/master/item/doc/grammar.txt&quot; target=&quot;_blank&quot;&gt;read the grammar here&lt;/a&gt;. This calls for a parser, which is another interesting topic for Hare, but I’ll set that aside for now and focus on the code gen. Assume that, given a file like the following, we can parse it and produce an AST:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;namespace hello;

interface hello {
	call say_hello() void;
	call add(a: uint, b: uint) uint;
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The key that makes the code gen approach we’re looking at today is the introduction of &lt;a href=&quot;https://docs.harelang.org/strings/template&quot; target=&quot;_blank&quot;&gt;strings::template&lt;/a&gt; to the Hare standard library. This module is inspired by a similar feature from Python, &lt;a href=&quot;https://docs.python.org/3/library/string.html#template-strings&quot; target=&quot;_blank&quot;&gt;string.Template&lt;/a&gt;. An example of its usage is provided in Hare’s standard library documentation:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;Hello&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;Your&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;balance&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;$$$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;balance&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;string_escape&quot;&gt;\n&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&amp;quot;&lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &amp;quot;&lt;span class=&quot;constant variable&quot;&gt;ddevault&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&amp;quot;&lt;span class=&quot;constant variable&quot;&gt;balance&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; 1000&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// &amp;quot;Hello, ddevault! Your balance is $1000.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Makes sense? Cool. Let’s see how this can be applied to code generation. The interface shown above compiles to the following generated code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// This file was generated by ipcgen; do not modify by hand&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;HELLO_ID&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0xC01CAAC5&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;fn_hello_say_hello&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword_function type&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant type variable&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;fn_hello_add&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword_function type&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant type variable&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;hello_iface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;say_hello&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_hello_say_hello&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_hello_add&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;hello_label&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SAY_HELLO&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;HELLO_ID&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;16u64&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;HELLO_ID&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;16u64&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;hello&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;_iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;hello_iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;_endpoint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;hello_dispatch&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;a1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;recvraw&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;_endpoint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;hello_label&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;hello_label&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SAY_HELLO&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;_iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;say_hello&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid_cslot&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// callee stored the reply&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;hello_label&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rval&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;_iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;a1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ipcbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rval&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid_cslot&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// callee stored the reply&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generating this code starts with the following entry-point:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Generates code for a server to implement the given interface.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;// This file was generated by ipcgen; do not modify by hand&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;use errors;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;use helios;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;use rt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;interfaces&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;iface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;interfaces&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;s_iface&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we start with some simple use of basic string formatting via &lt;a href=&quot;https://docs.harelang.org/fmt#fprintln&quot; target=&quot;_blank&quot;&gt;fmt::fprintln&lt;/a&gt;. We see some of the same approach repeated in the meatier functions like s_iface:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;s_iface&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;gen_name_upper&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;genhash&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fprintfln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;def {}_ID: u32 = 0x{:X};&lt;/span&gt;&lt;span class=&quot;error string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our first use of strings::template appears when we want to generate type aliases for interface functions, via s_method_fntype. This is where some of the trade-offs of this approach begin to present themselves.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s_method_fntype_src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
	&lt;span class=&quot;string&quot;&gt;`export type fn_$iface_$method = fn(object: *$object$params) $result;`&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;st_method_fntype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@init&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;s_method_fntype&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;st_method_fntype&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s_method_fntype_src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;s_method_fntype&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;caps_in&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;caps_out&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;{}: &amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;ipc_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;param_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;ipc_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;st_method_fntype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;iface&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;object&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;params&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;result&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The simple string substitution approach of strings::template prevents it from being as generally useful as a full-blown templating engine ala jinja2. To work around this, we have to write Hare code which does things like slurping up the method parameters into a &lt;a href=&quot;https://docs.harelang.org/strio#dynamic&quot; target=&quot;_blank&quot;&gt;strio::dynamic&lt;/a&gt; buffer where we might instead reach for something like &lt;code&gt;{% for param in method.params %}&lt;/code&gt; in jinja2. Once we have prepared all of our data in a format suitable for a linear string substitution, we can pass it to &lt;abbr title=&quot;This file aliases strings::template as tmpl to simplify things a bit.&quot;&gt;tmpl&lt;/abbr&gt;::execute. The actual template is stored in a global which is compiled during @init, which runs at program startup. Anything which requires a loop to compile, such as the parameter list, is fetched out of the strio buffer and passed to the template.&lt;/p&gt;&lt;p&gt;We can explore a slightly different approach when we generate this part of the code, back up in the s_iface function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;hello_iface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;say_hello&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_hello_say_hello&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;fn_hello_add&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To output this code, we render several templates one after another, rather than slurping up the generated code into heap-allocated string buffers to be passed into a single template.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s_iface_header_src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
	&lt;span class=&quot;string&quot;&gt;`export type $iface_iface = struct {`&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;st_iface_header&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s_iface_method_src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
	&lt;span class=&quot;string&quot;&gt;`	$method: *fn_$iface_$method,`&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;st_iface_method&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@init&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;s_iface&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;st_iface_header&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s_iface_header_src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;st_iface_method&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s_iface_method_src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;error constant variable&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;st_iface_header&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0z&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; i &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; 1&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;tmpl&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;st_iface_method&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iface&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;meth&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;string_escape&quot;&gt;\n&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://git.sr.ht/~sircmpwn/ipcgen/tree/2cdc53095a052b4f5ce3fdc6e410f2dd17eea54d/item/gen/server.ha&quot; target=&quot;_blank&quot;&gt;remainder of the code&lt;/a&gt; is fairly similar.&lt;/p&gt;&lt;p&gt;strings::template is less powerful than a more sophisticated templating system might be, such as Golang’s text/template. A more sophisticated templating engine could be implemented for Hare, but it would be more challenging — no reflection or generics in Hare — and would not be a great candidate for the standard library. This approach hits the sweet spot of simplicity and utility that we’re aiming for in the Hare stdlib. strings::template is implemented in &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare/tree/da003e45ced4991b1bae282169dcf942e1e4b235/item/strings/template/template.ha&quot; target=&quot;_blank&quot;&gt;a single ~180 line file&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I plan to continue polishing this tool so I can use it to describe interfaces for communications between userspace drivers and other low-level userspace services in my operating system. If you have any questions, feel free to post them on my public inbox, or shoot them over to my new &lt;a href=&quot;https://fosstodon.org/@drewdevault&quot; target=&quot;_blank&quot;&gt;fediverse account&lt;/a&gt;. Until next time!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Hare-codegen-v2/</link>
        
        <pubDate>Sat, 26 Nov 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Hare-codegen-v2/</guid>
      </item>
    
      <item>
        
        
          <title>In praise of Plan 9</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs&quot; target=&quot;_blank&quot;&gt;Plan 9&lt;/a&gt; is an operating system designed by Bell Labs. It’s the OS they wrote &lt;em&gt;after&lt;/em&gt; Unix, with the benefit of hindsight. It is the most interesting operating system that you’ve never heard of, and, in my opinion, the best operating system design to date. Even if you haven’t heard of Plan 9, the designers of whatever OS you &lt;em&gt;do&lt;/em&gt; use have heard of it, and have incorporated some of its ideas into your OS.&lt;/p&gt;&lt;p&gt;Plan 9 is a research operating system, and exists to answer questions about ideas in OS design. As such, the Plan 9 experience is in essence an exploration of the interesting ideas it puts forth. Most of the ideas are small. Many of them found a foothold in the broader ecosystem — UTF-8, goroutines, /proc, containers, union filesystems, these all have their roots in Plan 9 — but many of its ideas, even the good ones, remain unexplored outside of Plan 9. As a consequence, Plan 9 exists at the center of a fervor of research achievements which forms a unique and profoundly interesting operating system.&lt;/p&gt;&lt;p&gt;One example I often raise to illustrate the design ideals of Plan 9 is to compare its approach to network programming with that of the Unix standard, Berkeley sockets. BSD sockets fly in the face of Unix sensibilities and are quite alien on the system, though by now everyone has developed stockholm syndrome with respect to them so they don’t notice. When everything is supposed to be a file on Unix, why is it that the networking API is entirely implemented with special-purpose syscalls and ioctls? On Unix, creating a TCP connection involves calling the “socket” syscall to create a magic file descriptor, then the “connect” syscall to establish a connection. Plan 9 is much more Unix in its approach: you open /net/tcp/clone to reserve a connection, and read the connection ID from it. Then you open /net/tcp/n/ctl and write “connect 127.0.0.1!80” to it, where “n” is that connection ID. Now you can open /net/tcp/n/data and that file is a full-duplex stream. No magic syscalls, and you can trivially implement it in a shell script.&lt;/p&gt;&lt;p&gt;This composes elegantly with another idea from Plan 9: the 9P protocol. All file I/O on the entire system uses the 9P protocol, which defines operations like read and write. This protocol is network transparent, and you can mount remote servers into your filesystem namespace and access their files over 9P. You can do something similar on Unix, but on Plan 9 you get much more mileage from the idea because everything is &lt;em&gt;actually&lt;/em&gt; a file, and there are no magic syscalls or ioctls. For instance, your Ethernet interface is at /net/ether0, and everything in there is just a file. Say you want to establish a VPN: you simply mount a remote server’s /net/ether0 at /net/ether1, and now you have a VPN. That’s &lt;em&gt;it&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;The mountpoints are interesting as well, because they exist within a per-process filesystem namespace. Mounting filesystems does not require special permissions like on Unix, because these mounts only exist within the process tree that creates them, rather than modifying global state. The filesystems can also be implemented in userspace rather trivially via the 9P protocol, similar to FUSE but much more straightforward. Many programs provide a programmable/scriptable interface via a special filesystem such as this.&lt;/p&gt;&lt;p&gt;Userspace programs can also provide filesystems compatible with those normally implemented by kernel drivers, like /net/ether0, and provide these to processes in their namespace. For example, /dev/draw is analogous to a framebuffer device: you open it to write pixels to the screen. The window manager, Rio, implements a /dev/draw-like interface in userspace, then mounts it in the filesystem namespace of its children. All GUI programs can thus be run both on a framebuffer or in a window, without any awareness of which it’s using. The same is also true over the network: to implement VNC-like functionality, just mount your local /dev/draw and /dev/kbd on a remote server. Add /dev/audio if you like.&lt;/p&gt;&lt;p&gt;These ideas can also be built upon to form something resembling a container runtime, pre-dating even early concepts like BSD jails by several years, and implementing them much more effectively. Recall that everything &lt;em&gt;really is&lt;/em&gt; just a file on Plan 9, unlike Unix. Access to the hardware is provided through normal files, and per-process namespaces do not require special permissions to modify mountpoints. Making a container is thus trivial: just unmount all of the hardware you don’t want the sandboxed program to have access to. Done. You don’t even have to be root. Want to forward a TCP port? Write an implementation of /net/tcp which is limited to whatever ports you need — perhaps with just a hundred lines of shell scripting — and mount it into the namespace.&lt;/p&gt;&lt;p&gt;The shell, rc, is also wonderful. The debugger is terribly interesting, and its ideas didn’t seem to catch on with the likes of gdb. The editors, acme and sam, are also interesting and present a unique user interface that you can’t find anywhere else. The plumber is cool, it’s like “what if xdg-open was good actually”. The kernel is concise and a pleasure to read. The entire operating system, kernel &lt;em&gt;and&lt;/em&gt; userspace, can be built from source code on my 12 year old laptop in about 5 minutes. The network database, ndb, is brilliant. The entire OS is stuffed to the brim with interesting ideas, all of them implemented with elegance, conciseness, and simplicity.&lt;/p&gt;&lt;p&gt;Plan 9 failed, in a sense, because Unix was simply too big and too entrenched by the time Plan 9 came around. It was doomed by its predecessor. Nevertheless, its design ideas and implementation resonate deeply with me, and have provided an endless supply of inspiration for my own work. I think that everyone owes it to themselves to spend a few weeks messing around with and learning about Plan 9. The dream is kept alive by &lt;a href=&quot;http://9front.org/&quot; target=&quot;_blank&quot;&gt;9front&lt;/a&gt;, which is the most actively maintained fork of Plan 9 available today. Install it on your ThinkPad and mess around.&lt;/p&gt;&lt;p&gt;I will offer a caveat, however: leave your expectations at the door. Plan 9 is not Unix, it is not Unix-compatible, and it is certainly not yet another Linux distribution. Everything you’re comfortable and familiar with in your normal Unix setup will not translate to Plan 9. Come to Plan 9 empty handed, and let it fill those hands with its ideas. You will come away from the experience as a better programmer.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/In-praise-of-Plan-9/</link>
        
        <pubDate>Sat, 12 Nov 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/In-praise-of-Plan-9/</guid>
      </item>
    
      <item>
        
        
          <title>Notes from kernel hacking in Hare, part 3: serial driver</title>
          <description>
            &lt;p&gt;Today I would like to show you the implementation of the first userspace driver for Helios: a simple serial driver. All of the code we’re going to look at today runs in userspace, not in the kernel, so strictly speaking this should be “notes from OS hacking in Hare”, but I won’t snitch if you don’t.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: In the &lt;a href=&quot;https://drewdevault.com/blog/Kernel-hacking-with-Hare-part-2/&quot;&gt;previous entry&lt;/a&gt; to this series, I promised to cover the userspace threading API in this post. I felt like covering this instead. Sorry!&lt;/em&gt;&lt;/p&gt;&lt;p&gt;A serial port provides a simple protocol for transferring data between two systems. It generalizes a bit, but for our purposes we can just think of this as a terminal which you can use over a simple cable and a simple protocol. It’s a standard x86_64 feature (though one which has been out of style for a couple of decades now), and its simple design (and high utility) makes it a good choice for the first driver to write for &lt;a href=&quot;https://sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Helios&lt;/a&gt;. We’re going to look at the following details today:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The system’s initramfs&lt;/li&gt;&lt;li&gt;The driver loader&lt;/li&gt;&lt;li&gt;The serial driver itself&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The initramfs used by Helios, for the time being, is just a tarball. I imported &lt;a href=&quot;https://docs.harelang.org/format/tar&quot; target=&quot;_blank&quot;&gt;format::tar&lt;/a&gt; from the standard library, a module which I designed for this express purpose, and made a few minor tweaks to make it suitable for Helios’ needs. I also implemented seeking within a tar entry to make it easier to write an ELF loader from it. The bootloader loads this tarball into memory, the kernel provides page capabilities to init for it, and then we can map it into memory and study it, something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;map_range&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rd&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pulling a specific driver out of it looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Loads a driver from the bootstrap tarball.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;earlyload&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;bootstrapfs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;ltrim&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;/&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ent&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Invalid bootstrap.tar file&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;skip&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// TODO: Better error handling here&lt;/span&gt;
			&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;load_driver&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
				&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Failed to load driver from boostrap&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
				&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Failed to load driver from boostrap&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
				&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;task_resume&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Missing bootstrap driver&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code finds a file in the tarball with the given path (e.g. &lt;code&gt;drivers/serial&lt;/code&gt;), creates a process with the driver loader, then resumes the thread and the driver is running. Let’s take a look at that driver loader next. The load_driver entry point takes an I/O handle to an ELF file and loads it:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;load_driver&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loader&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;newloader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;earlyconf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;driver_earlyconfig&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;cspace_radix&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;load_earlyconfig&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;earlyconf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;newprocess&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;earlyconf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cspace_radix&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;load_config&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;regs&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;rip&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;e_entry&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;rsp&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;INIT_STACK_ADDR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;task_writeregisters&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is essentially a standard ELF loader, which it calls via the more general “newprocess” and “load” functions, but drivers have an extra concern: the driver manifest. The “load_earlyconfig” processes manifest keys which are necessary to configure prior to loading the ELF image, and the “load_config” function takes care of the rest of the driver configuration. The remainder of the code configures the initial thread.&lt;/p&gt;&lt;p&gt;The actual driver manifest is an INI file which is embedded in a special ELF section in driver binaries. The manifest for the serial driver looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[driver]
name=pcserial
desc=Serial driver for x86_64 PCs

[cspace]
radix=12

[capabilities]
0:serial =
1:note = 
2:cspace = self
3:ioport = min=3F8, max=400
4:ioport = min=2E8, max=2F0
5:irq = irq=3, note=1
6:irq = irq=4, note=1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Helios is a capability-oriented system, and in order to do anything useful, each process needs to have capabilities to work with. Each driver declares exactly what capabilities it needs and receives only these capabilities, and nothing else. This provides stronger isolation than Unix systems can offer (even with something like OpenBSD’s pledge(2)) — this driver cannot even allocate memory.&lt;/p&gt;&lt;p&gt;A standard x86_64 ISA serial port uses two I/O port ranges, 0x3F8-0x400 and 0x2E8-0x2F0, as well as two IRQs, IRQ 3 and 4, together providing support for up to four serial ports. The driver first requests a “serial” capability, which is a temporary design for an IPC endpoint that the driver will use to actually process read or write requests. This will be replaced with a more sophisticated device manager system in the future. It also creates a notification capability, which is later used to deliver the IRQs, and requests a capability for its own cspace so that it can manage capability slots. This will be necessary later on. Following this it requests capabilities for the system resources it needs, namely the necessary I/O ports and IRQs, the latter configured to be delivered to the notification in capability slot 1.&lt;/p&gt;&lt;p&gt;With the driver isolated in its own address space, running in user mode, and only able to invoke this set of capabilities, it’s very limited in what kind of exploits it’s vulnerable to. If there’s a vulnerability here, the worst that could happen is that a malicious actor on the other end of the serial port could crash the driver, which would then be rebooted by the service manager. On Linux, a bug in the serial driver can be used to compromise the entire system.&lt;/p&gt;&lt;p&gt;So, the driver loader parses this file and allocates the requested capabilities for the driver. I’ll skip most of the code, it’s just a boring INI file parser, but the important bit is the table for capability allocations:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;capconfigfn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword_function type&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;parameter constant type variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Note: keep these tables alphabetized&lt;/span&gt;
&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;capconfigtab&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;capconfigfn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;cspace&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cap_cspace&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;endpoint&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cap_endpoint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;ioport&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cap_ioport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;irq&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cap_irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;note&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cap_note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;serial&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cap_serial&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// TODO: More&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This table defines functions which, when a given INI key in the [capabilities] section is found, provisions the requested capabilities. This list is not complete; in the future all kernel objects will be added as well as userspace-defined interfaces (similar to serial) which implement various driver interfaces, such as ‘fs’ or ‘gpu’. Let’s start with the notification capability:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;cap_note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newnote&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copyto&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This capability takes no configuration arguments, so we first simply check that the value is empty. Then we create a notification, copy it into the driver’s capability space at the requested capability address, then destroy our copy. Simple!&lt;/p&gt;&lt;p&gt;The I/O port capability is a bit more involved: it does accept configuration parameters, namely what I/O port range the driver needs.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;cap_ioport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;min&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;max&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;have_min&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;have_max&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;tokenize&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next_token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;cut&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;min&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;have_min&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;max&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;have_max&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;stou16b&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;HEX&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;have_min&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;have_max&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ioport&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;ioctl_issue&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;INIT_CAP_IOCONTROL&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ioport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copyto&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ioport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we split the configuration string on commas and parse each as a key/value pair delimited by an equal sign (”=”), looking for a key called “min” and another called “max”. At the moment the config parsing is just implemented in this function directly, but in the future it might make sense to write a small abstraction for capability configurations like this. Once we know the I/O port range the user wants, then we issue an I/O port capability for that range and copy it into the driver’s cspace.&lt;/p&gt;&lt;p&gt;IRQs are a bit more involved still. An IRQ capability must be configured to deliver IRQs to a notification object.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;cap_irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;irq&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;have_irq&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;have_note&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// ...config string parsing omitted...&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_note&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copyfrom&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;CADDR_UNDEF&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;_note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ct&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;identify&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;_note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ct&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ctype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;NOTIFICATION&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;comment spell&quot;&gt;// TODO: More semantically meaningful errors would be nice&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;irq&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;irqctl_issue&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;INIT_CAP_IRQCONTROL&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_note&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copyto&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to do this, the driver loader copies the notification capability &lt;em&gt;from&lt;/em&gt; the driver’s cspace and into the loader’s cspace, then creates an IRQ with that notification. It copies the new IRQ capability into the driver, then destroys its own copy of the IRQ and notification.&lt;/p&gt;&lt;p&gt;In this manner, the driver can declaratively state which capabilities it needs, and the loader can prepare an environment for it with these capabilities prepared. Once these capabilities are present in the driver’s cspace, the driver can invoke them by addressing the numbered capability slots in a send or receive syscall.&lt;/p&gt;&lt;p&gt;To summarize, the loader takes an I/O object (which we know is sourced from the bootstrap tarball) from which an ELF file can be read, finds a driver manifest, then creates a process and fills the cspace with the requested capabilities, loads the program into its address space, and starts the process.&lt;/p&gt;&lt;p&gt;Next, let’s look at the serial driver that we just finished loading.&lt;/p&gt;&lt;p&gt;Let me first note that this serial driver is a proof-of-concept at this time. A future serial driver will take a capability for a device manager object, then probe each serial port and provision serial devices for each working serial port. It will define an API which supports additional serial-specific features, such as configuring the baud rate. For now, it’s pretty basic.&lt;/p&gt;&lt;p&gt;This driver implements a simple event loop:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Configure the serial port&lt;/li&gt;&lt;li&gt;Wait for an interrupt or a read/write request from the user&lt;/li&gt;&lt;li&gt;On interrupt, process the interrupt, writing buffered data or buffering readable data&lt;/li&gt;&lt;li&gt;On a user request, buffer writes or unbuffer reads&lt;/li&gt;&lt;li&gt;GOTO 2&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The driver starts by defining some constants for the capability slots we set up in the manifest:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;EP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IRQ&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;CSPACE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IRQ3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IRQ4&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It also defines some utility code for reading and writing to the COM registers, and constants for each of the registers defined by the interface.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// COM1 port&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;COM1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x3F8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// COM2 port&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;COM2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x2E8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Receive buffer register&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;RBR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Transmit holding regiser&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;THR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// ...other registers omitted...&lt;/span&gt;

&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ioports&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;COM1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// 3 is the I/O port capability address&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;COM2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ioports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ioports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;ioport_in8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;invalid port&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ioports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ioports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;ioport_out8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;invalid port&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also define some statically-allocated data structures to store state for each COM port, and a function to initialize the port:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;comport&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;wbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;COM1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;COM2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;com_init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;comment spell&quot;&gt;// Disable interrupts&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;LCR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x80&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;comment spell&quot;&gt;// Enable divisor mode&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;DL_LSB&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;comment spell&quot;&gt;// Div Low:  01: 115200 bps&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;DL_MSB&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;comment spell&quot;&gt;// Div High: 00&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;LCR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x03&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;comment spell&quot;&gt;// Disable divisor mode, set parity&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;FCR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0xC7&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;comment spell&quot;&gt;// Enable FIFO and clear&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ERBFI&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;	&lt;span class=&quot;comment spell&quot;&gt;// Enable read interrupt&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The basics are in place. Let’s turn our attention to the event loop.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;com_init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;com_init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;irq_ack&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;IRQ3&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;irq_ack&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;IRQ4&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;pollcap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;
		&lt;span class=&quot;constant type variable&quot;&gt;pollcap&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IRQ&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pollflags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RECV&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant type variable&quot;&gt;pollcap&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;EP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pollflags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RECV&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pollflags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RECV&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;poll_irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pollflags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RECV&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;poll_endpoint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We initialize two COM ports first, using the function we were just reading. Then we ACK any IRQs that might have already been pending when the driver starts up, and we enter the event loop proper. Here we are polling on two capabilities, the notification to which IRQs are delivered, and the endpoint which provides the serial driver’s external API.&lt;/p&gt;&lt;p&gt;The state for each serial port includes a read buffer and a write buffer, defined in the comport struct shown earlier. We configure the COM port to interrupt when there’s data available to read, then pull it into the read buffer. If we have pending data to write, we configure it to interrupt when it’s ready to write more data, otherwise we leave this interrupt turned off. The “poll_irq” function handles these interrupts:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;poll_irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;IRQ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;irq_ack&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;IRQ3&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;irq_ack&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;IRQ4&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;iir&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IIR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;iir&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;port_irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;iir&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;port_irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;iir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;iir&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;com_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;iir&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;com_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The IIR register is the “interrupt identification register”, which tells us why the interrupt occurred. If it was because the port is readable, we call “com_read”. If the interrupt occurred because the port is writable, we call “com_write”. Let’s start with com_read. This interrupt is always enabled so that we can immediately start buffering data as the user types it into the serial port.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Reads data from the serial port&amp;apos;s RX FIFO.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;com_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;LSR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;RBF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;RBF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;RBR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// If the buffer is full we just drop chars&lt;/span&gt;
			&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// This part will be explained later:&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;rconsume&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code is pretty simple. For as long as the COM port is readable, read a character from it. If there’s room in the read buffer, append this character to it.&lt;/p&gt;&lt;p&gt;How about writing? Well, we need some way to fill the write buffer first. This part is pretty straightforward:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Append data to a COM port read buffer, returning the number of bytes buffered&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// successfully.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;com_wbuffer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;com_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code just adds data to the write buffer, making sure not to exceed the buffer length (note that in Hare this would cause an assertion, not a buffer overflow). Then we call “com_write”, which does the actual writing to the COM port.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Writes data to the serial port&amp;apos;s TX FIFO.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;com_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;LSR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;THRE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;THRE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ier&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IER&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ier&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ETBEI&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;THR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ier&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IER&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;wpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ier&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ETBEI&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;comout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ier&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ETBEI&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the COM port is not ready to write data, we enable an interrupt which will tell us when it is and return. Otherwise, we write up to 16 bytes — the size of the COM port’s FIFO — and remove them from the write buffer. If there’s more data to write, we enable the write interrupt, or we disable it if there’s nothing left. When enabled, this will cause an interrupt to fire when (1) we have data to write and (2) the serial port is ready to write it, and our event loop will call this function again.&lt;/p&gt;&lt;p&gt;That covers all of the code for driving the actual serial port. What about the interface for someone to actually use this driver?&lt;/p&gt;&lt;p&gt;The “serial” capability defined in the manifest earlier is a temporary construct to provision some means of communicating with the driver. It provisions an endpoint capability (which is an IPC primitive on Helios) and stashes it away somewhere in the init process so that I can write some temporary test code to actually read or write to the serial port. Either request is done by “call”ing the endpoint with the desired parameters, which will cause the poll in the event loop to wake as the endpoint becomes receivable, calling “poll_endpoint”.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;poll_endpoint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;recv&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;EP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;com_wbuffer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;store_reply&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;CADDR_UNDEF&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;pending_read&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;constant field variable&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;rconsume&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO: error&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;“Calls” in Helios work similarly to seL4. Essentially, when you “call” an endpoint, the calling thread blocks to receive the reply and places a reply capability in the receiver’s thread state. The receiver then processes their message and “replies” to the reply capability to wake up the calling thread and deliver the reply.&lt;/p&gt;&lt;p&gt;The message label is used to define the requested operation. For now, 0 is read and 1 is write. For writes, we append the provided data to the write buffer and reply with the number of bytes we buffered, easy breezy.&lt;/p&gt;&lt;p&gt;Reads are a bit more involved. If we don’t immediately have any data in the read buffer, we have to wait until we do to reply. We copy the reply from its special slot in our thread state into our capability space, so we can use it later. This operation is why our manifest requires cspace = self. Then we store the reply capability and buffer in a variable and move on, waiting for a read interrupt. On the other hand, if there &lt;em&gt;is&lt;/em&gt; data buffered, we consume it and reply immediately.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;rconsume&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;comport&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;rpending&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;amt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Makes sense?&lt;/p&gt;&lt;p&gt;That basically covers the entire serial driver. Let’s take a quick peek at the other side: the process which wants to read from or write to the serial port. For the time being this is all temporary code to test the driver with, and not the long-term solution for passing out devices to programs. The init process keeps a list of serial devices configured on the system:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;serial&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;serials&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;serial&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;register_serial&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;serials&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;serial&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;ep&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function is called by the driver manifest parser like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;cap_serial&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ep&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newendpoint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copyto&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;register_serial&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We make use of the serial port in the init process’s main function with a little test loop to echo reads back to writes:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;bi&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;[init] Hello from Mercury!&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bootstrap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;bootstrapfs_init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bi&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;bootstrapfs_finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bootstrap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;earlyload&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bootstrap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;/drivers/serial&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;[init] begin echo serial port&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;serial_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;serial_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “serial_read” and “serial_write” functions are:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;serial_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newpage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;map_flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;W&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;page_unmap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// TODO: Multiple serial ports&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;serials&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x7fff70000000&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// XXX arbitrary address&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ipcbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;serial_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newpage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// TODO: Multiple serial ports&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;serials&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x7fff70000000&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// XXX arbitrary address&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;map_flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;W&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;page_unmap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is something interesting going on here. Part of this code is fairly obvious — we just invoke the IPC endpoint using helios::call, corresponding nicely to the other end’s use of helios::reply, with the buffer address and size. However, the buffer address presents a problem: this buffer is in the init process’s address space, so the serial port cannot read or write to it!&lt;/p&gt;&lt;p&gt;In the long term, a more sophisticated approach to shared memory management will be developed, but for testing purposes I came up with this solution. For writes, we allocate a new page, map it into our address space, and copy the data we want to write to it. Then we unmap it, map it into the serial driver’s address space instead, and perform the call. For reads, we allocate a page, map it into the serial driver, call the IPC endpoint, then unmap it from the serial driver, map it into our address space, and copy the data back out of it. In both cases, we destroy the page upon leaving this function, which frees the memory and automatically unmaps the page from any address space. Inefficient, but it works for demonstration purposes.&lt;/p&gt;&lt;p&gt;And that’s really all there is to it! Helios officially has its first driver. The next step is to develop a more robust solution for describing capability interfaces and device APIs, then build a PS/2 keyboard driver and a BIOS VGA mode 3 driver for driving the BIOS console, and combine these plus the serial driver into a tty on which we can run a simple shell.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Kernel-hacking-with-Hare-part-3/</link>
        
        <pubDate>Thu, 27 Oct 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Kernel-hacking-with-Hare-part-3/</guid>
      </item>
    
      <item>
        
        
          <title>TOTP for 2FA is incredibly easy to implement. So what&apos;s your excuse?</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Time-based_one-time_password&quot; target=&quot;_blank&quot;&gt;Time-based one-time passwords&lt;/a&gt; are one of the more secure approaches to 2FA — certainly much better than SMS. And it’s much easier to implement than SMS as well. The algorithm is as follows:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Divide the current Unix timestamp by 30&lt;/li&gt;&lt;li&gt;Encode it as a 64-bit big endian integer&lt;/li&gt;&lt;li&gt;Write the encoded bytes to a SHA-1 HMAC initialized with the TOTP shared key&lt;/li&gt;&lt;li&gt;Let offs = hmac[-1] &amp; 0xF&lt;/li&gt;&lt;li&gt;Let hash = decode hmac[offs .. offs + 4] as a 32-bit big-endian integer&lt;/li&gt;&lt;li&gt;Let code = (hash &amp; 0x7FFFFFFF) % 1000000&lt;/li&gt;&lt;li&gt;Compare this code with the user’s code&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You’ll need a little dependency to generate QR codes with the &lt;a href=&quot;https://github.com/google/google-authenticator/wiki/Key-Uri-Format&quot; target=&quot;_blank&quot;&gt;otpauth:// URL scheme&lt;/a&gt;, a little UI to present the QR code and store the shared secret in your database, and a quick update to your login flow, and then you’re good to go.&lt;/p&gt;&lt;p&gt;Here’s the implementation SourceHut uses in Python. I hereby release this code into the public domain, or creative commons zero, at your choice:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;base64&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;hashlib&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;hmac&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;totp&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;secret&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;token&lt;/span&gt;):
    &lt;span class=&quot;constructor constant variable&quot;&gt;tm&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;int&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;time&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;time&lt;/span&gt;() &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;30&lt;/span&gt;)
    &lt;span class=&quot;constructor constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;base64&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;b32decode&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;secret&lt;/span&gt;)

    &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;ix&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;range&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;):
        &lt;span class=&quot;constructor constant variable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;struct&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;pack&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;gt;q&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;tm&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;ix&lt;/span&gt;)
        &lt;span class=&quot;constructor constant variable&quot;&gt;hm&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;hmac&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;HMAC&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;key&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;b&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;hashlib&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;sha1&lt;/span&gt;).&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;digest&lt;/span&gt;()
        &lt;span class=&quot;constructor constant variable&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;hm&lt;/span&gt;[&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x0F&lt;/span&gt;
        &lt;span class=&quot;constructor constant variable&quot;&gt;truncatedHash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;hm&lt;/span&gt;[&lt;span class=&quot;constructor constant variable&quot;&gt;offset&lt;/span&gt;:&lt;span class=&quot;constructor constant variable&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;]
        &lt;span class=&quot;constructor constant variable&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;struct&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;unpack&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;gt;L&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;truncatedHash&lt;/span&gt;)[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]
        &lt;span class=&quot;constructor constant variable&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x7FFFFFFF&lt;/span&gt;
        &lt;span class=&quot;constructor constant variable&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1000000&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;code&lt;/span&gt;:
            &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;True&lt;/span&gt;

    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implementation has a bit of a tolerance added to make clock skew less of an issue, but that also means that the codes are longer-lived. Feel free to edit these tolerances if you so desire.&lt;/p&gt;&lt;p&gt;Here’s another one written in Hare, also public domain/CC-0.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;sha1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;base32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Computes a TOTP code for a given time and key.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;totp&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;instant&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hmac&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;sha1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;beputu64&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;sha1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SIZE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;offs&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0xF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mac&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;begetu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x7FFFFFFF&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@test&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;totp&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;secret&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;3N2OTFHXKLR2E3WNZSYQ====&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;base32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;decodestr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;base32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;std_encoding&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;from_unix&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1650183739&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;totp&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;29283&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In any language, TOTP is just a couple of dozen lines of code even if there isn’t already a library — and there is probably already a library. You don’t have to store temporary SMS codes in the database, you don’t have to worry about phishing, you don’t have to worry about SIM swapping, and you don’t have to sign up for some paid SMS API like Twilio. It’s more secure and it’s trivial to implement — so implement it already! Please!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Update 2022-10-19 @ 07:45 UTC&lt;/strong&gt;: A reader pointed out that it’s important to have rate limiting on your TOTP attempts, or else a brute force attack can be effective. Fair point!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/TOTP-is-easy/</link>
        
        <pubDate>Tue, 18 Oct 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/TOTP-is-easy/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, October 2022</title>
          <description>
            &lt;p&gt;After a few busy and stressful months, I decided to set aside October to rest. Of course, for me, rest does not mean a cessation of programming, but rather a shift in priorities towards more fun and experimental projects. Consequently, it has been a great month for Helios!&lt;/p&gt;&lt;p&gt;Hare upstream has enjoyed some minor improvements, such as from Pierre Curto’s patch to support parsing IPv6 addresses with a port (e.g. “[::1]:80”) and Kirill Primak’s improvements to the UTF-8 decoder. On the whole, improvements have been conservative. However, queued up for integration once qbe upstream support is merged is support for @threadlocal variables, which are useful for Helios and for ABI compatibility with C. I also drafted up a proof-of-concept for @inline functions, but it still needs work.&lt;/p&gt;&lt;p&gt;Now for the main event: Helios. The large-scale redesign and refactoring I mentioned in the previous status update is essentially complete, and the kernel reached (and exceeded) feature parity with the previous status quo. Since Helios has been my primary focus for the past couple of weeks, I have a lot of news to share about it.&lt;/p&gt;&lt;p&gt;First, I got back into userspace a few days after the last status update, and shortly thereafter implemented a new scheduler. I then began to rework the userspace API (uapi) in the kernel, which differs substantially from its prior incarnation. The kernel object implementations present themselves as a library for kernel use, and the new uapi module handles all interactions with this module from userspace, providing a nice separation of concerns. The uapi module handles more than syscalls now — it also implements send/recv for kernel objects, for instance. As of a few days ago, uapi also supports delivering faults to userspace supervisor processes:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/52ee4c38.png&quot;&gt;
&lt;figcaption&gt;A screenshot of a thread on Helios causing a page fault, then its parent thread receives details of the fault and maps a page onto the address of the attempted write. The child thread is resumed and is surprised to find that the write succeeded (because a page was mapped underneath the write).&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;attribute&quot;&gt;@test&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pagefault&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fault&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newendpoint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fault&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;_task_pagefault&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;set_fault&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fault&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fault&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;recv_fault&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fault&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fault&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x100&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newpage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;map_flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;map_flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;FIXED&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;resume&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;_task_pagefault&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x100&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1337&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1337&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new userspace threading API is much improved over the hack job in the earlier design. It supports TLS and many typical threading operations, such as join and detach. This API exists mainly for testing the kernel via Vulcan, and is not anticipated to see much use beyond this (though I will implement pthreads for the POSIX C environment at some point). For more details, see &lt;a href=&quot;https://drewdevault.com/2022/10/02/Kernel-hacking-with-Hare-part-2.html&quot; target=&quot;_blank&quot;&gt;this blog post&lt;/a&gt;. Alongside this and other userspace libraries, Vulcan has been fleshed out into a kernel test suite once again, which I have been frequently testing on real hardware:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/3a293b79.jpg&quot;&gt;
&lt;figcaption&gt;A picture of a laptop showing 15 passing kernel tests&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://redacted.moe/f/f95549d6.iso&quot; target=&quot;_blank&quot;&gt;Here’s an ISO&lt;/a&gt; you can boot on your own x86_64 hardware to see if it works for you, too. If you have problems, take a picture of the issue, boot Linux and &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email me&lt;/a&gt; said picture, the output of lscpu, and any other details you deem relevant.&lt;/p&gt;&lt;p&gt;The kernel now supports automatic capability address allocation, which is a marked improvement over seL4. The new physical page allocator is also much improved, as it supports allocation and freeing and can either allocate pages sparsely or continuously depending on the need. Mapping these pages in userspace was also much improved, with a better design of the userspace virtual memory map and a better heap, complete with a (partial) implementation of mmap.&lt;/p&gt;&lt;p&gt;I have also broken ground on the next component of the OS, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mercury&quot; target=&quot;_blank&quot;&gt;Mercury&lt;/a&gt;, which provides a more complete userspace environment for writing drivers. It has a simple tar-based initramfs based on Hare’s format::tar implementation, which I wrote in June for this purpose. It can load ELF files from this tarball into new processes, and implements some extensions that are useful for driver loading. Consequently, the first Mercury driver is up and running:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/4294d07e.png&quot;&gt;
&lt;figcaption&gt;Demo of a working serial driver&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;This driver includes a simple driver manifest, which is embedded into its ELF file and processed by the driver loader to declaratively specify the capabilities it needs:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pcserial&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x86_64&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;PCs&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cspace&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;radix&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;12

&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;capabilities&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioport&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;F8&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;400
2&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioport&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;2E8&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;F0&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
3&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 
4&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;3
5&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The driver loader prepares capabilities for the COM1 and COM2 I/O ports, as well as IRQ handlers for IRQ 3 and 4, based on this manifest, then loads them into the capability table for the driver process. The driver is sandboxed very effectively by this: it can &lt;em&gt;only&lt;/em&gt; use these capabilities. It cannot allocate memory, modify its address space, or even destroy any of these capabilities. If a bad actor was on the other end of the serial port and exploited a bug, the worst thing it could do is crash the serial driver, which would then be rebooted by the supervisor. On Linux and other monolithic kernels like it, exploiting the serial driver compromises the entire operating system.&lt;/p&gt;&lt;p&gt;The resulting serial driver implementation is pretty small and straightforward, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mercury/tree/master/item/drivers/x86_64/serial&quot; target=&quot;_blank&quot;&gt;if you’d like to have a look&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This manifest format will be expanded in the future for additional kinds of drivers, such as with details specific to each bus (i.e. PCI vendor information or USB details), and will also have details for device trees when RISC-V and ARM support (the former is already underway) are brought upstream.&lt;/p&gt;&lt;p&gt;Next steps are to implement an I/O abstraction on top of IPC endpoints, which first requires call &amp; reply support — the latter was implemented last night and requires additional testing. Following this, I plan on writing a getty-equivalent which utilizes this serial driver, and a future VGA terminal driver, to provide an environment in which a shell can be run. Then I’ll implement a ramfs to host commands for the shell to run, and we’ll really be cookin’ at that point. Disk drivers and filesystem drivers will be next.&lt;/p&gt;&lt;p&gt;That’s all for now. Quite a lot of progress! I’ll see you next time.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-October-2022/</link>
        
        <pubDate>Sat, 15 Oct 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-October-2022/</guid>
      </item>
    
      <item>
        
        
          <title>In praise of ffmpeg</title>
          <description>
            &lt;p&gt;My last “&lt;a href=&quot;https://drewdevault.com/2022/09/02/2022-09-02-In-praise-of-qemu.html&quot; target=&quot;_blank&quot;&gt;In praise of&lt;/a&gt;” article covered qemu, a project founded by Fabrice Bellard, and today I want to take a look at another work by Bellard: &lt;a href=&quot;https://ffmpeg.org&quot; target=&quot;_blank&quot;&gt;ffmpeg&lt;/a&gt;. Bellard has a knack for building high-quality software which solves a problem so well that every other solution becomes obsolete shortly thereafter, and ffmpeg is no exception.&lt;/p&gt;&lt;p&gt;ffmpeg has been described as the Swiss army knife of multimedia. It incorporates hundreds of video, audio, and image decoders and encoders, muxers and demuxers, filters and devices. It provides a CLI and a set of libraries for working with its tools, and is the core component of many video and audio players as a result (including my preferred multimedia player, &lt;a href=&quot;https://mpv.io&quot; target=&quot;_blank&quot;&gt;mpv&lt;/a&gt;). If you want to do almost anything with multimedia files — re-encode them, re-mux them, live stream it, whatever — ffmpeg can handle it with ease.&lt;/p&gt;&lt;p&gt;Let me share an example.&lt;/p&gt;&lt;p&gt;I was recently hanging out at my local hackerspace and wanted to play some PS2 games on my laptop. My laptop is not powerful enough to drive &lt;a href=&quot;https://pcsx2.net&quot; target=&quot;_blank&quot;&gt;PCSX2&lt;/a&gt;, but my workstation on the other side of town certainly was. So I forwarded my game controller to my workstation via USB/IP and pulled up the ffmpeg manual to figure out how to live-stream the game to my laptop. ffmpeg can capture video from KMS buffers directly, use the GPU to efficiently downscale them, grab audio from pulse, encode them with settings tuned for low-latency, and mux it into a UDP socket. On the other end I set up mpv to receive the stream and play it back.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ffmpeg \
  -f pulse \
  -i alsa_output.platform-snd_aloop.0.analog-surround-51.monitor \
  -f kmsgrab \
  -thread_queue_size 64 \   # reduce input latency
  -i - \
  # Capture and downscale frames on the GPU:
  -vf &amp;apos;hwmap=derive_device=vaapi,scale_vaapi=1280:720,hwdownload,format=bgr0&amp;apos; \
  -c:v libx264 \
  -preset:v superfast \     # encode video as fast as possible
  -tune zerolatency \       # tune encoder for low latency
  -intra-refresh 1 \        # reduces latency and mitigates dropped packets
  -f mpegts \               # mux into mpegts stream, well-suited to this use-case
  -b:v 3M \                 # configure target video bandwidth
  udp://$hackerspace:41841
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With an hour of tinkering and reading man pages, I was able to come up with a single command which produced a working remote video game streaming setup &lt;em&gt;from scratch&lt;/em&gt; thanks to ffmpeg. ffmpeg is &lt;em&gt;amazing&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;video loop autoplay muted controls&gt;
&lt;source src=&quot;https://redacted.moe/f/3c729511.webm&quot;&gt;
&lt;/video&gt;&lt;/p&gt;&lt;p&gt;I have relied on ffmpeg for many tasks and for many years. It has always been there to handle any little multimedia-related task I might put it to for personal use — re-encoding audio files so they fit on my phone, taking clips from videos to share, muxing fonts into mkv files, capturing video from my webcam, &lt;a href=&quot;https://drewdevault.com/2018/08/26/Self-hosted-livestreaming.html&quot; target=&quot;_blank&quot;&gt;live streaming hacking sessions on my own platform&lt;/a&gt;, or anything else I can imagine. It formed the foundation of &lt;a href=&quot;https://github.com/mediacrush/mediacrush&quot; target=&quot;_blank&quot;&gt;MediaCrush&lt;/a&gt; back in the day, where we used it to optimize multimedia files for efficient viewing on the web, back when that was more difficult than “just transcode it to a webm”.&lt;/p&gt;&lt;p&gt;ffmpeg is notable for being one of the first large-scale FOSS projects to completely eradicate proprietary software in its niche. Virtually all multimedia-related companies rely on ffmpeg to do their heavy lifting. It took a complex problem and solved it, with free software. The book is now closed on multimedia: ffmpeg is the solution to almost all of your problems. And if it’s not, you’re more likely to patch ffmpeg than to develop something new. The code is accessible and the community are experts in your problem domain.&lt;/p&gt;&lt;p&gt;ffmpeg is one of the foremost pillars of achievement in free software. It has touched the lives of every reader, whether they know it or not. If you’ve ever watched TV, or gone to a movie, or watched videos online, or listened to a podcast, odds are that ffmpeg was involved in making it possible. It is one of the most well-executed and important software projects of all time.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/In-praise-of-ffmpeg/</link>
        
        <pubDate>Wed, 12 Oct 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/In-praise-of-ffmpeg/</guid>
      </item>
    
      <item>
        
        
          <title>Does Rust belong in the Linux kernel?</title>
          <description>
            &lt;p&gt;I am known to be a bit of a polemic when it comes to Rust. I will be forthright with the fact that I don’t particularly care for Rust, and that my public criticisms of it might set up many readers with a reluctance to endure yet another Rust Hot Take from my blog. My answer to the question posed in the title is, of course, “no”. However, let me assuage some of your fears by answering a different question first: does Hare belong in the Linux kernel?&lt;/p&gt;&lt;p&gt;If I should owe my allegiance to any programming language, it would be &lt;a href=&quot;https://harelang.org&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt;. Not only is it a systems programming language that I designed myself, but I am using it &lt;a href=&quot;https://git.sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;to write a kernel&lt;/a&gt;. &lt;a href=&quot;https://www.redox-os.org/&quot; target=&quot;_blank&quot;&gt;Like Rust&lt;/a&gt;, Hare is demonstrably useful for writing kernels with. One might even go so far as to suggest that I consider it superior to C for this purpose, given that I chose to to write Helios in Hare rather than C, despite my extensive background in C. But the question remains: does Hare belong in the Linux kernel?&lt;/p&gt;&lt;p&gt;In my opinion, Hare does not belong in the Linux kernel, and neither does Rust. Some of the reasoning behind this answer is common to both, and some is unique to each, but I will be focusing on Rust today because Rust is the language which is actually making its way towards mainline Linux. I have no illusions about this blog post changing that, either: I simply find it an interesting case-study in software engineering decision-making in a major project, and that’s worth talking about.&lt;/p&gt;&lt;p&gt;Each change in software requires sufficient supporting rationale. What are the reasons to bring Rust into Linux? A kernel hacker thinks about these questions differently than a typical developer in userspace. One could espouse the advantages of Cargo, generics, whatever, but these concerns matter relatively little to kernel hackers. Kernels operate in a heavily constrained design space and a language has to fit into that design space. This is the first and foremost concern, and if it’s awkward to mold a language to fit into these constraints then it will be a poor fit.&lt;/p&gt;&lt;p&gt;Some common problems that a programming language designed for userspace will run into when being considered for kernelspace are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Strict constraints on memory allocation&lt;/li&gt;&lt;li&gt;Strict constraints on stack usage&lt;/li&gt;&lt;li&gt;Strict constraints on recursion&lt;/li&gt;&lt;li&gt;No use of floating point arithmetic&lt;/li&gt;&lt;li&gt;Necessary evils, such as unsafe memory use patterns or integer overflow&lt;/li&gt;&lt;li&gt;The absence of a standard library, runtime, third-party libraries, or other conveniences typically afforded to userspace&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Most languages can overcome these constraints with some work, but their suitability for kernel use is mainly defined by how well they adapt to them — there’s a reason that kernels written in Go, C#, Java, Python, etc, are limited to being research curiosities and are left out of production systems.&lt;/p&gt;&lt;p&gt;As Linus recently put it, “kernel needs trump any Rust needs”. The kernel is simply not an environment which will bend to accommodate a language; it must go the other way around. These constraints have posed, and will continue to pose, a major challenge for Rust in Linux, but on the whole, I think that it will be able to rise to meet them, though perhaps not with as much grace as I would like.&lt;/p&gt;&lt;p&gt;If Rust is able to work within these constraints, then it satisfies the ground rules for playing in ring 0. The question then becomes: what advantages can Rust bring to the kernel? Based on what I’ve seen, these essentially break down to two points:&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Memory safety&lt;/li&gt;&lt;li&gt;Trendiness&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I would prefer not to re-open the memory safety flamewar, so we will simply move forward with the (dubious) assumptions that memory safety is (1) unconditionally desirable, (2) compatible with the kernel’s requirements, and (3) sufficiently provided for by Rust. I will offer this quote from an unnamed kernel hacker, though:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;There are possibly some well-designed and written parts which have not suffered a memory safety issue in many years. It’s insulting to present this as an improvement over what was achieved by those doing all this hard work.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Regarding “trendiness”, I admit that this is a somewhat unforgiving turn of phrase. In this respect I refer to the goal of expanding the kernel’s developer base from a bunch of aging curmudgeons writing C&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; towards a more inclusive developer pool from a younger up-and-coming language community like Rust. C is boring&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; — it hasn’t really excited anyone in decades. Rust is exciting, and its community enjoys a huge pool of developers building their brave new world with it. Introducing Rust to the kernel will certainly appeal to a broader audience of potential contributors.&lt;/p&gt;&lt;p&gt;But there is an underlying assumption to this argument which is worth questioning: is the supply of Linux developers dwindling, and, if so, is it to such and extent that it demands radical change?&lt;/p&gt;&lt;p&gt;Well, no. Linux has consistently enjoyed a tremendous amount of attention from the software development community. This week’s release of Linux 6.0, one of the largest Linux releases ever, boasted more than 78,000 commits by almost 5,000 different authors since 5.15. Linux has a broad developer base reaching from many different industry stakeholders and independent contributors working on the careful development and maintenance of its hundreds of subsystems. The scale of Linux development is on a level unmatched by any other software project — free software or otherwise.&lt;/p&gt;&lt;p&gt;Getting Rust working in Linux is certainly an exciting project, and I’m all for developers having fun. However, it’s not likely to infuse Linux with a much-needed boost in its contributor base, because Linux has no such need. What’s more, Linux’s portability requirements prevent Rust from being used in most of the kernel in the first place. Most work on Rust in Linux is simply working on getting the systems to cooperate with each other or writing drivers which are redundant with existing C drivers, but cannot replace them due to Rust’s limited selection of targets.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; Few to none of the efforts from the Rust-in-Linux team are likely to support the kernel’s broader goals for some time.&lt;/p&gt;&lt;p&gt;We are thus left with memory safety as the main benefit offered by Rust to Linux, and for the purpose of this article we’re going to take it at face value. So, with the ground rules set and the advantages enumerated, what are some of the problems that Rust might face in Linux?&lt;/p&gt;&lt;p&gt;There are a few problems which could be argued over, such as substantial complexity of Rust compared to C, the inevitable doubling of Linux’s build time, the significant shift in design sensibilities required to support an idiomatic Rust design, the fragile interface which will develop on the boundaries between Rust and C code, or the challenges the kernel’s established base of C developers will endure when learning and adapting to a new language. To avoid letting this post become too subjective or lengthy, I’ll refrain from expanding on these. Instead, allow me to simply illuminate these issues as risk factors.&lt;/p&gt;&lt;p&gt;Linux is, on the whole, a conservative project. It is deployed worldwide in billions of devices and its reliability is depended on by a majority of Earth’s population. Risks are carefully evaluated in Linux as such. Every change presents risks and offers advantages, which must be weighed against each other to justify the change. Rust is one of the riskiest bets Linux has ever considered, and, in my opinion, the advantages may not weigh up. I think that the main reason we’re going to see Rust in the kernel is not due to a careful balancing of risk and reward, but because the Rust community wants Rust in Linux, and they’re large and loud enough to not be worth the cost of arguing with.&lt;/p&gt;&lt;p&gt;I don’t think that changes on this scale are appropriate for most projects. I prefer to encourage people to write new software to replace established software, rather than rewriting the established software. Some projects, such as &lt;a href=&quot;https://www.redox-os.org/&quot; target=&quot;_blank&quot;&gt;Redox&lt;/a&gt;, are doing just that with Rust. However, operating systems are in a difficult spot in this respect. Writing an operating system is difficult work with a huge scope — few projects can hope to challenge Linux on driver support, for example. The major players have been entrenched for decades, and any project seeking to displace them will have decades of hard work ahead of them and will require a considerable amount of luck to succeed. Though I think that new innovations in kernels are badly overdue, I must acknowledge that there is some truth to the argument that we’re stuck with Linux. In this framing, if you want Rust to succeed in a kernel, getting it into Linux is the best strategy.&lt;/p&gt;&lt;p&gt;But, on the whole, my opinion is that the benefits of Rust in Linux are negligible and the costs are not. That said, it’s going to happen, and the impact to me is likely to be, at worst, a nuisance. Though I would have chosen differently, I wish them the best of luck and hope to see them succeed.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Does-Rust-belong-in-Linux/</link>
        
        <pubDate>Mon, 03 Oct 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Does-Rust-belong-in-Linux/</guid>
      </item>
    
      <item>
        
        
          <title>Notes from kernel hacking in Hare, part 2: multi-threading</title>
          <description>
            &lt;p&gt;I have long promised that Hare would not have multi-threading, and it seems that I have broken that promise. However, I have remained true to the not-invented-here approach which is typical of my style by introducing it only after designing an entire kernel to implement it on top of.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;For some background, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Helios&lt;/a&gt; is a micro-kernel written in Hare. In addition to the project, the &lt;a href=&quot;https://git.sr.ht/~sircmpwn/helios/tree/master/item/vulcan&quot; target=&quot;_blank&quot;&gt;Vulcan&lt;/a&gt; system is a small userspace designed to test the kernel.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/a8018226.jpg&quot;&gt;
&lt;figcaption&gt;A picture of a laptop running Helios and showing the results of the Vulcan test suite&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;While I don’t anticipate multi-threaded processes playing a huge role in the complete Ares system in the future, they do have a place. In the long term, I would like to be able to provide an implementation of pthreads for porting existing software to the system. A more immediate concern is how to test the various kernel primitives provided by Helios, such as those which facilitate inter-process communication (IPC). It’s much easier to test these with threads than with processes, since spawning threads does not require spinning up a new address space.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;attribute&quot;&gt;@test&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newnote&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;notification_wait&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;notification_wait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So how does it work? Let’s split this up into two domains: kernelspace and userspace.&lt;/p&gt;&lt;h3&gt;Threads in the kernel&lt;/h3&gt;&lt;p&gt;The basic primitive for threads and processes in Helios is a “task”, which is simply an object which receives some CPU time. A task has a capability space (so it can invoke operations against kernel objects), an virtual address space (so it has somewhere to map the process image and memory), and some state, such as the values of its CPU registers. The task-related structures are:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// A task capability.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;caps&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;capability&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	@offset&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;caps&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;LINK_OFFS&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;caps&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Scheduling status of a task.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;task_status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ACTIVE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;BLOCKED&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type comment spell&quot;&gt;// XXX: Can a task be both blocked and suspended?&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SUSPENDED&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// State for a task.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;taskstate&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;cspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;caps&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cslot&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;caps&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cslot&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;ipc_buffer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;task_status&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// XXX: This is a virtual address, should be physical&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;taskstate&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;prev&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;taskstate&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s a footnote to explain some off-topic curiosities about this code: &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The most interesting part of this structure is arch::state, which stores the task’s CPU registers. On x86_64,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; this structure is defined as follows:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;fsbase&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;

	&lt;span class=&quot;constant field variable&quot;&gt;r15&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;r14&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;r13&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;r12&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;r11&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;r10&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;r9&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;r8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rdx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rcx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rbx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;

	&lt;span class=&quot;constant field variable&quot;&gt;intno&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;errcode&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;

	&lt;span class=&quot;constant field variable&quot;&gt;rip&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;cs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rflags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This structure is organized in part according to hardware constraints and in part at the discretion of the kernel implementer. The last five fields, from %rip to %ss, are constrained by the hardware. When an interrupt occurs, the CPU pushes each of these registers to the stack, in this order, then transfers control to the system interrupt handler. The next two registers serve a special purpose within our interrupt implementation, and the remainder are ordered arbitrarily.&lt;/p&gt;&lt;p&gt;In order to switch between two tasks, we need to save all of this state somewhere, then load the same state for another task when returning from the kernel to userspace. The save/restore process is handled in the interrupt handler, in assembly:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;.global isr_common
isr_common:
	_swapgs
	push %rax
	push %rbx
	push %rcx
	push %rdx
	push %rsi
	push %rdi
	push %rbp
	push %r8
	push %r9
	push %r10
	push %r11
	push %r12
	push %r13
	push %r14
	push %r15

	// Note: fsbase is handled elsewhere
	push $0
	push %fs

	cld

	mov %rsp, %rdi
	mov $_kernel_stack_top, %rsp
	call arch.isr_handler
_isr_exit:
	mov %rax, %rsp

	// Note: fsbase is handled elsewhere
	pop %fs
	pop %r15

	pop %r15
	pop %r14
	pop %r13
	pop %r12
	pop %r11
	pop %r10
	pop %r9
	pop %r8
	pop %rbp
	pop %rdi
	pop %rsi
	pop %rdx
	pop %rcx
	pop %rbx
	pop %rax

	_swapgs

	// Clean up error code and interrupt #
	add $16, %rsp

	iretq
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I’m not going to go into too much detail on interrupts for this post (maybe in a later post), but what’s important here is the chain of push/pop instructions. This automatically saves the CPU state for each task when entering the kernel. The syscall handler has something similar.&lt;/p&gt;&lt;p&gt;This suggests a question: where’s the stack?&lt;/p&gt;&lt;p&gt;Helios has a single kernel stack,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; which is moved to %rsp from $_kernel_stack_top in this code. This is different from systems like Linux, which have one kernel stack per thread; the rationale behind this design choice is out of scope for this post.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; However, the “stack” being pushed to here is not, in fact, a traditional stack.&lt;/p&gt;&lt;p&gt;x86_64 has an interesting feature wherein an interrupt can be configured to use a special “interrupt stack”. The &lt;a href=&quot;https://wiki.osdev.org/Task_State_Segment&quot; target=&quot;_blank&quot;&gt;task state segment&lt;/a&gt; is a bit of a historical artifact which is of little interest to Helios, but in long mode (64-bit mode) it serves a new purpose: to provide a list of addresses where up to seven interrupt stacks are stored. The &lt;a href=&quot;https://wiki.osdev.org/Interrupt_Descriptor_Table#Structure_on_x86-64&quot; target=&quot;_blank&quot;&gt;interrupt descriptor table&lt;/a&gt; includes a 3-bit “IST” field which, when nonzero, instructs the CPU to set the stack pointer to the corresponding address in the TSS when that interrupt fires. Helios sets all of these to one, then does something interesting:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Stores a pointer to the current state context.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;init_tss&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;cpus&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;tstate&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;taskstate&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cpus&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;tstate&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;ist&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// On x86_64, most registers are saved and restored by the ISR or&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// syscall service routines.&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;regs&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fsbase&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;rdmsr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0xC0000100&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;wrmsr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0xC0000100&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fsbase&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;regs&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We store a pointer to the active task’s state struct in the TSS when we enter userspace, and when an interrupt occurs, the CPU automatically places that state into %rsp so we can trivially push all of the task’s registers into it.&lt;/p&gt;&lt;p&gt;There is some weirdness to note here: the stack grows downwards. Each time you push, the stack pointer is decremented, then the pushed value is written there. So, we have to fill in this structure from the bottom up. Accordingly, we have to do something a bit unusual here: we don’t store a pointer to the context object, but a pointer to the &lt;em&gt;end&lt;/em&gt; of the context object. This is what &amp;active[-1] does here.&lt;/p&gt;&lt;p&gt;Hare has some memory safety features by default, such as bounds testing array accesses. Here we have to take advantage of some of Hare’s escape hatches to accomplish the goal. First, we cast the pointer to an &lt;em&gt;unbounded array&lt;/em&gt; of states — that’s what the *[*] is for. Then we can take the address of element -1 without the compiler snitching on us.&lt;/p&gt;&lt;p&gt;There is also a separate step here to save the fsbase register. This will be important later.&lt;/p&gt;&lt;p&gt;This provides us with enough pieces to enter userspace:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Immediately enters this task in userspace. Only used during system&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// initialization.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@noreturn&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;enteruser&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;caps&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;capability&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;task_getstate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;task_schedulable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;vspace_activate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;enteruser&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What we need next is a scheduler, and a periodic interrupt to invoke it, so that we can switch tasks every so often.&lt;/p&gt;&lt;p&gt;Scheduler design is a complex subject which can have design, performance, and complexity implications ranging from subtle to substantial. For Helios’s present needs we use a simple round-robin scheduler: each task gets the same time slice and we just switch to them one after another.&lt;/p&gt;&lt;p&gt;The easy part is simply getting periodic interrupts. Again, this blog post isn’t about interrupts, so I’ll just give you the reader’s digest:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;install_irq&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;PIT_IRQ&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pit_irq&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pit_setphase&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;pit_irq&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;irq&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;sched&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;switchtask&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;pic_eoi&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PIT_IRQ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The PIT, or programmable interrupt timer, is a feature on x86_64 which provides exactly what we need: periodic interrupts. This code configures it to tick at 100 Hz and sets up a little IRQ handler which calls sched::switchtask to perform the actual context switch.&lt;/p&gt;&lt;p&gt;Recall that, by the time sched::switchtask is invoked, the CPU and interrupt handler have already stashed all of the current task’s registers into its state struct. All we have to do now is pick out the next task and restore its state.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// see idle.s&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;idle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Switches to the next task.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;switchtask&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// Save state&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;taskstate&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;vspace_activate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;idle&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;taskstate&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cand&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;taskstate&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;task_schedulable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cand&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cand&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cand&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;taskstate&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;task_schedulable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty straightforward. The scheduler &lt;a href=&quot;https://git.sr.ht/~sircmpwn/helios/tree/673c27e57684deeedbe88e1e6b7b940fdca5fc87/item/sched/tasks.ha&quot; target=&quot;_blank&quot;&gt;maintains a linked list&lt;/a&gt; of tasks, picks the next one which is schedulable,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; then runs it. If there are no schedulable tasks, it runs the idle task.&lt;/p&gt;&lt;p&gt;Err, wait, what’s the idle task? Simple: it’s another state object (i.e. a set of CPU registers) which essentially works as a statically allocated do-nothing thread.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;idle_frame&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Initializes the state for the idle thread.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;init_idle&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;idle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;idle&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;cs&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;seg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;KCODE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;ss&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;seg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;KDATA&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;rflags&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;rip&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;rbp&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;idle_frame&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;“pause” is a simple loop:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;hlt&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;jmp&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pause&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the situation where every task is blocking on I/O, there’s nothing for the CPU to do until the operation finishes. So, we simply halt and wait for the next interrupt to wake us back up, hopefully unblocking some tasks so we can schedule them again. A more sophisticated kernel might take this opportunity to go into a lower power state, perhaps, but for now this is quite sufficient.&lt;/p&gt;&lt;p&gt;With this last piece in place, we now have a multi-threaded operating system. But there is one more piece to consider: when a task yields its time slice.&lt;/p&gt;&lt;p&gt;Just because a task receives CPU time does not mean that it needs to use it. A task which has nothing useful to do can yield its time slice back to the kernel through the “yieldtask” syscall. On the face of it, this is quite simple:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Yields the current time slice and switches to the next task.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@noreturn&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;yieldtask&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;sysret_set&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;switchtask&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;enteruser&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “sysret_set” updates the registers in the task state which correspond with system call return values to (0, 0), indicating a successful return from the yield syscall. But we don’t actually return at all: we switch to the next task and then return to &lt;em&gt;that&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;In addition to being called from userspace, this is also useful whenever the kernel blocks a thread on some I/O or IPC operation. For example, tasks can wait on “notification” objects, which another task can signal to wake them up — a simple synchronization primitive. The implementation makes good use of sched::yieldtask:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Blocks the active task until this notification is signalled. Does not return&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// if the operation is blocking.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;caps&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;capability&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;nbwait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;would_block&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;note_getstate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;recv&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO: support multiple receivers&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;recv&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;sched&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;sched&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;task_status&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;BLOCKED&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;sched&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;yieldtask&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, that’s the last piece.&lt;/p&gt;&lt;h3&gt;Threads in userspace&lt;/h3&gt;&lt;p&gt;Phew! That was a lot of kernel pieces to unpack. And now for userspace… in the next post! This one is getting pretty long. Here’s what you have to look forward to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Preparing the task and all of the objects it needs (such as a stack)&lt;/li&gt;&lt;li&gt;High-level operations: join, detach, exit, suspend, etc&lt;/li&gt;&lt;li&gt;Thread-local storage…&lt;ul&gt;&lt;li&gt;in the Hare compiler&lt;/li&gt;&lt;li&gt;in the ELF loader&lt;/li&gt;&lt;li&gt;at runtime&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Putting it all together to test the kernel&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We’ll see you next time!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Kernel-hacking-with-Hare-part-2/</link>
        
        <pubDate>Sun, 02 Oct 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Kernel-hacking-with-Hare-part-2/</guid>
      </item>
    
      <item>
        
        
          <title>The phrase &quot;open source&quot; (still) matters</title>
          <description>
            &lt;p&gt;In 1988, “Resin Identification Codes” were introduced by the plastic industry. These look exactly like the recycling symbol ♺, which is not trademarked or regulated, except that a number is enclosed within the triangle. These symbols simply identify what kind of plastic was used. The vast majority of plastic is non-recyclable, but has one of these symbols on it to suggest otherwise. This is a deceptive business practice which exploits the consumer’s understanding of the recycling symbol to trick them into buying more plastic products.&lt;/p&gt;&lt;p&gt;The meaning of the term “open source” is broadly understood to be defined by the Open Source Initiative’s &lt;a href=&quot;https://opensource.org/osd&quot; target=&quot;_blank&quot;&gt;Open Source Definition&lt;/a&gt;, the “OSD”. Under this model, open source has enjoyed a tremendous amount of success, such that virtually all software written today incorporates open source components.&lt;/p&gt;&lt;p&gt;The main advantage of open source, to which much of this success can be attributed, is that it is a product of many hands. In addition to the work of its original authors, open source projects generally accept code contributions from anyone who would offer them. They also enjoy numerous indirect benefits, through the large community of Linux distros which package and ship the software, or people who write docs or books or blog posts about it, or the many open source dependencies it is likely built on top of.&lt;/p&gt;&lt;p&gt;Under this model, the success of an open source project is not entirely attributable to its publisher, but to both the publisher and the community which exists around the software. The software does not belong to its publisher, but to its community. I mean this not only in a moral sense, but also in a legal sense: every contributor to an open source project retains their copyright and the project’s ownership is held collectively between its community of contributors.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The OSD takes this into account when laying out the conditions for commercialization of the software. An argument for exclusive commercialization of software by its publishers can be made when the software is the result of investments from that publisher alone, but this is not so for open source. Because it is the product of its community as a whole, the community enjoys the right to commercialize it, without limitation. This is a fundamental, non-negotiable part of the open source definition.&lt;/p&gt;&lt;p&gt;However, we often see the odd company or organization trying to forward an unorthodox definition of the “open source”. Generally, their argument goes something like this: “open” is just an adjective, and “source” comes from “source code”, so “open source” just means source code you can read, right?&lt;/p&gt;&lt;p&gt;This argument is wrong,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; but it usually conceals the speaker’s real motivations: they want a commercial monopoly over their project.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Their real reason is “I should be able to make money from open source, but you shouldn’t”. An argument for an unorthodox definition of “open source” from this perspective is a form of &lt;a href=&quot;https://en.wikipedia.org/wiki/Motivated_reasoning&quot; target=&quot;_blank&quot;&gt;motivated reasoning&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Those making this argument have good reason to believe that they will enjoy more business success if they get away with it. The open source brand is incredibly strong — one of the most successful brands in the entire software industry. Leveraging that brand will drive interest to their project, especially if, on the surface, it looks like it fits the bill (generally by being &lt;abbr
title=&quot;The appropriate term for software whose source code is available to the public, but which does not otherwise meet the Open Source Definition&quot;&gt;source available&lt;/abbr&gt;).&lt;/p&gt;&lt;p&gt;When you get down to it, this behavior is dishonest and anti-social. It leverages the brand of open source, whose success has been dependent on the OSD and whose brand value is associated with the user’s understanding of open source, but does not provide the same rights. The deception is motivated by selfish reasons: to withhold those rights from the user for their own exclusive use. This is wrong.&lt;/p&gt;&lt;p&gt;You can publish software under any terms that you wish, with or without commercial rights, with or without source code, whatever — it’s your right. However, if it’s not open source, it’s wrong to call it open source. There are better terms — “source available”, “&lt;a href=&quot;https://faircode.io/&quot; target=&quot;_blank&quot;&gt;fair code&lt;/a&gt;”, etc. If you describe your project appropriately, whatever the license may be, then I wish you nothing but success.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Open-source-matters/</link>
        
        <pubDate>Fri, 16 Sep 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Open-source-matters/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, September 2022</title>
          <description>
            &lt;p&gt;I have COVID-19 and I am halfway through my stockpile of tissues, so I’m gonna keep this status update brief.&lt;/p&gt;&lt;p&gt;In Hare news, I finally put the last pieces into place to make &lt;a href=&quot;https://harelang.org/blog/2022-09-06-cross-builds-with-hare/&quot; target=&quot;_blank&quot;&gt;cross compiling&lt;/a&gt; as easy as possible. Nothing else particularly world-shattering going on here. I have a bunch of new stuff in my patch queue to review once I’m feeling better, however, including bigint stuff — a big step towards TLS support. Unrelatedly, TLS support seems to be progressing upstream in qbe. (See what I did there?)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/powerctl&quot; target=&quot;_blank&quot;&gt;powerctl&lt;/a&gt; is a small new project I wrote to configure power management states on Linux. I’m pretty pleased with how it turned out. It makes for a good &lt;a href=&quot;https://drewdevault.com/2022/08/28/powerctl-a-hare-case-study.html&quot; target=&quot;_blank&quot;&gt;case study&lt;/a&gt; on Hare for systems programming.&lt;/p&gt;&lt;p&gt;In Helios, I have been refactoring the hell out of everything, rewriting or redesigning large parts of it from scratch. Presently this means that a lot of the functionality which was previously present was removed, and is being slowly re-introduced with substantial changes. The key is reworking these features to take better consideration of the full object lifecycle — creating, copying, and destroying capabilities. An improvement which ended up being useful in the course of this work is adding address space IDs (PCIDs on x86_64), which is going to offer a substantial performance boost down the line.&lt;/p&gt;&lt;p&gt;Alright, time for a nap. Bye!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-September-2022/</link>
        
        <pubDate>Thu, 15 Sep 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-September-2022/</guid>
      </item>
    
      <item>
        
        
          <title>Notes from kernel hacking in Hare, part 1</title>
          <description>
            &lt;p&gt;One of the goals for the &lt;a href=&quot;https://harelang.org/&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt; programming language is to be able to write kernels, such as my &lt;a href=&quot;https://git.sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Helios&lt;/a&gt; project. Kernels are complex beasts which exist in a somewhat unique problem space and have constraints that many userspace programs are not accustomed to. To illustrate this, I’m going to highlight a scenario where Hare’s low-level types and manual memory management approach shines to enable a difficult use-case.&lt;/p&gt;&lt;p&gt;Helios is a micro-kernel. During system initialization, its job is to load the initial task into memory, prepare the initial set of kernel objects for its use, provide it with information about the system, then jump to userspace and fuck off until someone needs it again. I’m going to focus on the “providing information” step here.&lt;/p&gt;&lt;p&gt;The information the kernel needs to provide includes details about the capabilities that init has access to (such as working with I/O ports), information about system memory, the address of the framebuffer, and so on. This information is provided to init in the bootinfo structure, which is mapped into its address space, and passed to init via a register which points to this structure.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// The bootinfo structure.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;bootinfo&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// Capability ranges&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;devmem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;userimage&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;unused&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// Other details&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;arch_bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;ipcbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;module_desc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;memory_info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;memory_desc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;devmem_info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;memory_desc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;tls_base&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;tls_size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parts of this structure are static (such as the capability number ranges for each capability assigned to init), and others are dynamic - such as structures describing the memory layout (N items where N is the number of memory regions), or the kernel command line. But, we’re in a kernel – dynamically allocating data is not so straightforward, especially for units smaller than a page!&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; Moreover, the data structures allocated here need to be visible to userspace, and kernel memory is typically not available to userspace. A further complication is the three different address spaces we’re working with here: a bootinfo object has a physical memory address, a kernel address, and a userspace address — three addresses to refer to a single object in different contexts.&lt;/p&gt;&lt;p&gt;Here’s an example of what the code shown in this article is going to produce:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/ad558439.png&quot;&gt;
&lt;figcaption&gt;A 64 by 64 grid of cells representing a page of physical memory. The first set of cells are colored blue; the next set green; then purple; the remainder are brown.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;This is a single page of physical memory which has been allocated for the bootinfo data, where each cell is a byte. The bootinfo structure itself comes first, in blue. Following this is an arch-specific bootinfo structure, in green:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// x86_64-specific boot information&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;arch_bootinfo&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;type comment spell&quot;&gt;// Page table capabilities&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;pdpt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;pt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;cap_range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// vbe_mode_info physical address from multiboot (or zero)&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;vbe_mode_info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After this, in purple, is the kernel command line. These three structures are always consistently allocated for any boot configuration, so the code which sets up the bootinfo page (the code we’re going to read now) always provisions them. Following these three items is a large area of free space (indicated in brown) which will be used to populate further dynamically allocated bootinfo structures, such as descriptions of physical memory regions.&lt;/p&gt;&lt;p&gt;The code to set this up is &lt;code&gt;bootinfo_init&lt;/code&gt;, which is responsible for allocating a suitable page, filling in the bootinfo structure, and preparing a vector to dynamically allocate additional data on this page. It also sets up the arch bootinfo and argv, so the page looks like this diagram when the function returns. And here it is, in its full glory:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Initializes the bootinfo context.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;bootinfo_init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;bootinfo_ctx&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cslot&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;caps&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cslot&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;ctype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PAGE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cslot&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;heap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;phys&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;page_phys&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;mem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;phys_tokernel&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;phys&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bisz&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bootvec&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bisz&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;bootinfo_ctx&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cslot&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;arch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;arch_bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// Fixed up below&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;bootvec&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bootvec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;mkbootvec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;arch_bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;arch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;arch_bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;arch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;arch_bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;mkbootvec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;toutf8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;argv&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;capacity&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first three lines are fairly straightforward. Helios uses capability-based security, similar in design to &lt;a href=&quot;https://sel4.systems/&quot; target=&quot;_blank&quot;&gt;seL4&lt;/a&gt;. All kernel objects — such as pages of physical memory — are utilized through the capability system. The first two lines set aside a slot to store the page capability in, then allocate a page using that slot. The next two lines grab the page’s physical address and use &lt;code&gt;mem::phys_tokernel&lt;/code&gt; to convert it to an address in the kernel’s virtual address space, so that the kernel can write data to this page.&lt;/p&gt;&lt;p&gt;The next two lines are where it starts to get a little bit interesting:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;bisz&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; size&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bootinfo&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bootvec&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;bisz&lt;/span&gt;&lt;span class=&quot;error punctuation_special type&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special type&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This casts the “info” variable (of type *bootinfo) to a pointer to an &lt;em&gt;unbounded&lt;/em&gt; array of bytes (*[*]u8). This is a little bit dangerous! Hare’s arrays are bounds tested by default and using an unbounded type disables this safety feature. We want to get a bounded slice again soon, which is what the first slicing operator here does: &lt;code&gt;[bisz..arch::PAGESIZE]&lt;/code&gt;. This obtains a &lt;em&gt;bounded&lt;/em&gt; slice of bytes which starts from the end of the bootinfo structure and continues to the end of the page.&lt;/p&gt;&lt;p&gt;The last expression, another slicing expression, is a little bit unusual. A slice type in Hare has the following internal representation:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;slice&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;capacity&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you slice an unbounded array, you get a slice whose length and capacity fields are equal to the length of the slicing operation, in this case &lt;code&gt;arch::PAGESIZE - bisz&lt;/code&gt;. But when you slice a &lt;em&gt;bounded&lt;/em&gt; slice, the length field takes on the length of the slicing expression but the capacity field is calculated from the original slice. So by slicing our new bounded slice to the 0th index ([..0]), we obtain the following slice:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bootinfo&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;capacity&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;PAGESIZE&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bisz&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In plain English, this is a slice whose base address is the address following the bootinfo structure and whose capacity is the remainder of the free space on its page, with a length of zero. This is something we can use &lt;span
class=&quot;rainbow&quot;&gt;static append&lt;/span&gt; with!&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;style&gt;
.rainbow {
  background-image: linear-gradient(to left, violet, indigo, blue, green, yellow, orange, red);
  background-clip: text;
  background-size: 800% 800%;
  animation: rainbow 8s ease infinite;
  -webkit-text-fill-color: transparent;
}

@keyframes rainbow { 
    0%{background-position:0% 50%}
    50%{background-position:100% 25%}
    100%{background-position:0% 50%}
}
&lt;/style&gt;
&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Allocates a buffer in the bootinfo vector, returning the kernel vector and a&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// pointer to the structure in the init vspace.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;mkbootvec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;bootinfo_ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;prevlen&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;bootvec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;padding&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;prevlen&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;al&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;padding&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;al&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;prevlen&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;bootvec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vec&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;bootvec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;prevlen&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;INIT_BOOTINFO_ADDR&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;prevlen&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Hare, slices can be dynamically grown and shrunk using the &lt;em&gt;append&lt;/em&gt;, &lt;em&gt;insert&lt;/em&gt;, and &lt;em&gt;delete&lt;/em&gt; keywords. This is pretty useful, but not applicable for our kernel — remember, no dynamic memory allocation. Attempting to use append in Helios would cause a linking error because the necessary runtime code is absent from the kernel’s Hare runtime. However, you can also &lt;em&gt;statically&lt;/em&gt; append to a slice, as shown here. So long as the slice has a sufficient capacity to store the appended data, a static append or insert will succeed. If not, an assertion is thrown at runtime, much like a normal bounds test.&lt;/p&gt;&lt;p&gt;This function makes good use of it to dynamically allocate memory from the bootinfo page. Given a desired size and alignment, it statically appends a suitable number of zeroes to the page, takes a slice of the new data, and returns both that slice (in the kernel’s address space) and the address that data will have in the user address space. If we return to the earlier function, we can see how this is used to allocate space for the arch_bootinfo structure:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mkbootvec&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; size&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch_bootinfo&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; size&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;arch_bootinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;arch_bootinfo&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “ctx” variable is used by the kernel to keep track of its state while setting up the init task, and we stash the kernel’s pointer to this data structure in there, and the user’s pointer in the bootinfo structure itself.&lt;/p&gt;&lt;p&gt;This is also used to place argv into the bootinfo page:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mkbootvec&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;toutf8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;error constant variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;capacity&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we allocate a vector whose length is the length of the argument string, with an alignment of one, and then copy argv into it as a UTF-8 slice. Slice copy expressions like this one are a type-safe and memory-safe way to memcpy in Hare. Then we do something a bit more interesting.&lt;/p&gt;&lt;p&gt;Like slices, strings have an internal representation in Hare which includes a data pointer, length, and capacity. The types module provides a struct with this representation so that you can do low-level string manipulation in Hare should the task call for it. Hare’s syntax allows us to take the address of a literal value, such as a types::string struct, using the &amp; operator. Then we cast it to a pointer to a string and dereference it. Ta-da! We set the bootinfo argv field to a str value which uses the user address of the argument vector.&lt;/p&gt;&lt;p&gt;Some use-cases call for this level of fine control over the precise behavior of your program. Hare’s goal is to accommodate this need with little fanfare. Here we’ve drawn well outside of the lines of Hare’s safety features, but sometimes it’s useful and necessary to do so. And Hare provides us with the tools to get the safety harness back on quickly, such as we saw with the construction of the bootvec slice. This code is pretty weird but to an experienced Hare programmer (which, I must admit, the world has very few of) it should make sense.&lt;/p&gt;&lt;p&gt;I hope you found this interesting! I’m going back to kernel hacking. Next up is loading the userspace ELF image into its address space. I had this working before but decided to rewrite it. Wish me good luck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Kernel-hacking-with-Hare-part-1/</link>
        
        <pubDate>Wed, 07 Sep 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Kernel-hacking-with-Hare-part-1/</guid>
      </item>
    
      <item>
        
        
          <title>In praise of qemu</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://www.qemu.org/&quot; target=&quot;_blank&quot;&gt;qemu&lt;/a&gt; is another in a long line of great software started by &lt;a href=&quot;https://bellard.org/&quot; target=&quot;_blank&quot;&gt;Fabrice Bellard&lt;/a&gt;. It provides virtual machines for a wide variety of software architectures. Combined with KVM, it forms the foundation of nearly all cloud services, and it runs SourceHut in our self-hosted datacenters. Much like Bellard’s ffmpeg revolutionized the multimedia software industry, qemu revolutionized virtualisation.&lt;/p&gt;&lt;p&gt;qemu comes with a large variety of studiously implemented virtual devices, from standard real-world hardware like e1000 network interfaces to accelerated virtual hardware like virtio drives. One can, with the right combination of command line arguments, produce a virtual machine of essentially any configuration, either for testing novel configurations or for running production-ready virtual machines. Network adapters, mouse &amp; keyboard, IDE or SCSI or SATA drives, sound cards, graphics cards, serial ports — the works. Lower level, often arch-specific features, such as AHCI devices, SMP, NUMA, and so on, are also available and invaluable for testing any conceivable system configurations. And these configurations &lt;em&gt;work&lt;/em&gt;, and work reliably.&lt;/p&gt;&lt;p&gt;I have relied on this testing quite a bit when working on kernels, particularly on &lt;a href=&quot;https://drewdevault.com/2022/06/13/helios.html&quot; target=&quot;_blank&quot;&gt;my own Helios kernel&lt;/a&gt;. With a little bit of command line magic, I can run a fully virtualised system with a serial driver connected to the parent terminal, with a hardware configuration appropriate to whatever I happen to be testing, in a manner such that running and testing my kernel is no different from running any other program. With -gdb I can set up gdb remote debugging and even debug my kernel as if it were a typical program. Anyone who remembers osdev in the Bochs days — or even earlier — understands the unprecedented luxury of such a development environment. Should I ever find myself working on a hardware configuration which is unsupported by qemu, my very first step will be patching qemu to support it. In my reckoning, qemu support is nearly as important for bringing up a new system as a C compiler is.&lt;/p&gt;&lt;p&gt;And qemu’s implementation in C is simple, robust, and comprehensive. On the several occasions when I’ve had to read the code, it has been a pleasure. Furthermore, the comprehensive approach allows you to build out a virtualisation environment tuned precisely to your needs, whatever they may be, and it is accommodating of many needs. Sure, it’s low level — running a qemu command line is certainly more intimidating than, say, VirtualBox — but the trade-off in power afforded to the user opens up innumerable use-cases that are simply not available on any other virtualisation platform.&lt;/p&gt;&lt;p&gt;One of my favorite, lesser-known features of qemu is qemu-user, which allows you to register a &lt;a href=&quot;https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html&quot; target=&quot;_blank&quot;&gt;binfmt&lt;/a&gt; handler to run executables compiled for an arbitrary architecture on Linux. Combined with a little chroot, this has made cross-arch development easier than it has ever been before, something I frequently rely on when working on &lt;a href=&quot;https://harelang.org/&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt;. If you do cross-architecture work and you haven’t set up qemu-user yet, you’re missing out.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ uname -a
Linux taiga 5.15.63-0-lts #1-Alpine SMP Fri, 26 Aug 2022 07:02:59 +0000 x86_64 GNU/Linux
$ doas chroot roots/alpine-riscv64/ /bin/sh
# uname -a
Linux taiga 5.15.63-0-lts #1-Alpine SMP Fri, 26 Aug 2022 07:02:59 +0000 riscv64 Linux
&lt;/code&gt;&lt;/pre&gt;&lt;!-- Inline styles because lazy --&gt;
&lt;small style=&quot;
  text-align: center;
  display: block;
  color: #555;
&quot;&gt;This is amazing.&lt;/small&gt;
&lt;p&gt;qemu also holds a special place in my heart as one of the first projects I contributed to over email 🙂 And they still use email today, and even &lt;a href=&quot;https://qemu.readthedocs.io/en/v6.2.0/devel/submitting-a-patch.html#if-you-cannot-send-patch-emails&quot; target=&quot;_blank&quot;&gt;recommend SourceHut&lt;/a&gt; to make the process easier for novices.&lt;/p&gt;&lt;p&gt;So, to Fabrice, and the thousands of other contributors to qemu, I offer my thanks. qemu is one of my favorite pieces of software.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/In-praise-of-qemu/</link>
        
        <pubDate>Fri, 02 Sep 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/In-praise-of-qemu/</guid>
      </item>
    
      <item>
        
        
          <title>powerctl: A small case study in Hare for systems programming</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/powerctl/&quot; target=&quot;_blank&quot;&gt;powerctl&lt;/a&gt; is a little weekend project I put together to provide a simple tool for managing power states on Linux. I had previously put my laptop into suspend with a basic “echo mem | doas tee /sys/power/state”, but this leaves a lot to be desired. I have to use doas to become root, and it’s annoying to enter my password — not to mention difficult to use in a script or to attach to a key binding. powerctl is the solution: a small 500-line Hare program which provides comprehensive support for managing power states on Linux for non-privileged users.&lt;/p&gt;&lt;p&gt;This little project ended up being a useful case-study in writing a tight systems program in Hare. It has to do a few basic tasks which Hare shines in:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;setuid binaries&lt;/li&gt;&lt;li&gt;Group lookup from /etc/group&lt;/li&gt;&lt;li&gt;Simple string manipulation&lt;/li&gt;&lt;li&gt;Simple I/O within sysfs constraints&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Linux documents these features &lt;a href=&quot;https://www.kernel.org/doc/html/latest/admin-guide/pm/sleep-states.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;, so it’s a simple matter of rigging it up to a nice interface. Let’s take a look at how it works.&lt;/p&gt;&lt;p&gt;First, one of the base requirements for this tool is to run as a non-privileged user. However, since writing to sysfs requires root, this program will have to be setuid, so that it runs as root regardless of who executes it. To prevent any user from suspending the system, I added a “power” group and only users who are in this group are allowed to use the program. Enabling this functionality in Hare is quite simple:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;POWER_GROUP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;power&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Determines if the current user is a member of the power group.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;checkgroup&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;uid&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;getuid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;euid&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;geteuid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;uid&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;euid&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Error: this program must be installed with setuid (chmod u+s)&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;getgroup&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;POWER_GROUP&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;grent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;grent&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;grent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Error: {} group missing from /etc/group&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;POWER_GROUP&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;grent_finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;gids&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;getgroups&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;gids&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;gids&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The POWER_GROUP variable allows distributions that package powerctl to configure exactly which group is allowed to use this tool. Following this, we compare the uid and effective uid. If the uid is zero, we’re already running this tool as root, so we move on. Otherwise, if the euid is nonzero, we lack the permissions to continue, so we bail out and tell the user to fix their installation.&lt;/p&gt;&lt;p&gt;Then we fetch the details for the power group from /etc/group. Hare’s standard library includes &lt;a href=&quot;https://docs.harelang.org/unix/passwd&quot; target=&quot;_blank&quot;&gt;a module&lt;/a&gt; for working with this file. Once we have the group ID from the string, we check the current user’s supplementary group IDs to see if they’re a member of the appropriate group. Nice and simple. This is also the only place in powerctl where dynamic memory allocation is required, to store the group details, which are freed with “defer passwd::grent_finish”.&lt;/p&gt;&lt;p&gt;The tool also requires some simple string munging to identify the supported set of states. If we look at /sys/power/disk, we can see the kind of data we’re working with:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ cat /sys/power/disk 
[platform] shutdown reboot suspend test_resume 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These files are a space-separated list of supported states, with the currently enabled state enclosed in square brackets. Parsing these files is a simple matter for Hare. We start with a simple utility function which reads the file and prepares a &lt;a href=&quot;https://docs.harelang.org/strings#tokenize&quot; target=&quot;_blank&quot;&gt;string tokenizer&lt;/a&gt; which splits the string on spaces:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;read_states&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;tokenizer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Unexpected EOF from sysfs&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;rtrim&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fromutf8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;character string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;tokenize&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The error handling here warrants a brief note. This function can fail if the file does not exist or if there is an I/O error when reading it. I don’t think that I/O errors are possible in this specific case (they can occur when &lt;em&gt;writing&lt;/em&gt; to these files, though), but we bubble it up regardless using “io::read()?”. The file might not exist if these features are not supported by the current kernel configuration, in which case it’s bubbled up as “errors::noentry” via “os::open()?”. These cases are handled further up the call stack. The other potential error site is “io::close”, which can fail but only in certain circumstances (such as closing the same file twice), and we use the error assertion operator (”!”) to indicate that the programmer believes this case cannot occur. The compiler will check our work and abort at runtime should this assumption be proven wrong in practice.&lt;/p&gt;&lt;p&gt;In the happy path, we read the file, trim off the newline, and return a tokenizer which splits on spaces. The storage for this string is borrowed from “buf”, which is statically allocated.&lt;/p&gt;&lt;p&gt;The usage of this function to query supported disk suspend behaviors is here:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;read_disk_states&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;disk_state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;disk_state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;read_states&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;/sys/power/disk&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;states&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;disk_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;disk_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next_token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;trimmed&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;[&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;]&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;trimmed&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;platform&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;disk_state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PLATFORM&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;shutdown&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;disk_state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SHUTDOWN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;reboot&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;disk_state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;REBOOT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;suspend&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;disk_state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SUSPEND&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;test_resume&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;disk_state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;TEST_RESUME&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;states&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;trimmed&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;states&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function returns a tuple which includes all of the supported disk states OR’d together, and a value which indicates which state is currently enabled. The loop iterates through each of the tokens from the tokenizer returned by &lt;code&gt;read_states&lt;/code&gt;, trims off the square brackets, and adds the appropriate state bits. We also check the trimmed token against the original token to detect which state is currently active.&lt;/p&gt;&lt;p&gt;There’s two edge cases to be taken into account here: what happens if Linux adds more states in the future, and what happens if none of the states are active? In the former case, we have the &lt;code&gt;continue&lt;/code&gt; branch of the switch statement mid-loop. Hare requires all switch statements to be exhaustive, so the compiler forces us to consider this edge case. For the latter case, the return value will be zero, simply indicating that none of these states are active. This is not actually possible given the invariants for this kernel interface, but we could end up in this situation if the kernel adds a new disk mode &lt;em&gt;and&lt;/em&gt; that disk mode is active when this code runs.&lt;/p&gt;&lt;p&gt;When the time comes to modify these states, either to put the system to sleep or to configure its behavior when put to sleep, we use the following function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;write_state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;WRONLY&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;TRUNC&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;buffered&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code is working within a specific constraint of sysfs: it must complete the write operation in a single syscall. One of Hare’s design goals is giving you sufficient control over the program’s behavior to plan for such concerns. The means of opening the file — WRONLY | TRUNC — was also chosen deliberately. The “single syscall” is achieved by using a buffered file, which soaks up writes until the buffer is full and then flushes them out all at once. The buffered stream flushes automatically on newlines by default, so the “ln” of “fprintln” causes the write to complete in a single call.&lt;/p&gt;&lt;p&gt;With this helper in place, we can write power states. The ones which configure the kernel, but don’t immediately sleep, are straightforward:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Sets the current mem state.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;set_mem_state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;mem_state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;write_state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;/sys/power/mem_sleep&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;mem_state_unparse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The star of the show, however, has some extra concerns:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Sets the current sleep state, putting the system to sleep.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;set_sleep_state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;sleep_state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// Sleep briefly so that the keyboard driver can process the key up if&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// the user runs this program from the terminal.&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;250&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;MILLISECOND&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;write_state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;/sys/power/state&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;sleep_state_unparse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you enter sleep with a key held down, key repeat will kick in for the duration of the sleep, so when running this from the terminal you’ll resume to find a bunch of new lines. The time::sleep call is a simple way to avoid this, by giving the system time to process your key release event before sleeping. A more sophisticated solution could open the uinput devices and wait for all keys to be released, but that doesn’t seem entirely necessary.&lt;/p&gt;&lt;p&gt;Following this, we jump into the dark abyss of a low-power coma.&lt;/p&gt;&lt;p&gt;And that’s all there is to it! A few hours of work and 500 lines of code later and we have a nice little systems program to make suspending my laptop easier. I was pleasantly surprised to find out how well this little program plays to Hare’s strengths. I hope you found it interesting! And if you happen to need a simple tool for suspending your Linux machines, &lt;a href=&quot;https://sr.ht/~sircmpwn/powerctl&quot; target=&quot;_blank&quot;&gt;powerctl&lt;/a&gt; might be the program for you.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/powerctl-a-hare-case-study/</link>
        
        <pubDate>Sun, 28 Aug 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/powerctl-a-hare-case-study/</guid>
      </item>
    
      <item>
        
        
          <title>A review of postmarketOS on the Xiaomi Poco F1</title>
          <description>
            &lt;p&gt;I have recently had cause to start looking into mainline Linux phones which fall outside of the common range of grassroots phones like the PinePhone (which was my daily driver for the past year). The &lt;a href=&quot;https://wiki.postmarketos.org/wiki/Devices&quot; target=&quot;_blank&quot;&gt;postmarketOS wiki&lt;/a&gt; is a great place to research candidate phones for this purpose, and the phone I landed on is the &lt;a href=&quot;https://wiki.postmarketos.org/wiki/Xiaomi_Poco_F1_(xiaomi-beryllium)&quot; target=&quot;_blank&quot;&gt;Xiaomi Poco F1&lt;/a&gt;, which I picked up on Amazon.nl (for ease of return in case it didn’t work out) for 270 Euro. Phones of this nature have a wide range of support from Linux distros like postmarketOS, from “not working at all” to “mostly working”. The essential features I require in a daily driver phone are (1) a working modem and telephony support, (2) mobile data, and (3) reasonably good performance and battery life; plus of course some sane baseline expectations like a working display and touchscreen driver.&lt;/p&gt;&lt;p&gt;The use of mainline Linux on a smartphone requires a certain degree of bullshit tolerance, and the main question is whether or not the bullshit exceeds your personal threshold. The Poco F1 indeed comes with some bullshit, but I’m pleased to report that it falls short of my threshold and represents a significant quality-of-life improvement over the PinePhone setup I have been using up to now.&lt;/p&gt;&lt;p&gt;The bullshit I have endured for the Poco F1 setup can be categorized into two parts: initial setup and ongoing problems. Of the two, the initial setup is by far the worst. These phones are designed to run Android first, rather than the mainline Linux first approach seen in devices like the PinePhone and Librem 5. This means that it’s back to dealing with things like Android recovery, fastboot, and so on, during the initial setup. The most severe pain point for Xiaomi phones is unlocking the bootloader.&lt;/p&gt;&lt;p&gt;The only officially supported means of doing this is via a Windows-only application published by Xiaomi. A &lt;a href=&quot;https://github.com/francescotescari/XiaoMiToolV2&quot; target=&quot;_blank&quot;&gt;reverse engineered&lt;/a&gt; Java application supposedly provides support for completing this process on Linux. However, this approach comes with the typical bullshit of setting up a working Java environment, and, crucially, Xiaomi appears to have sabotaged this effort via a deliberate attempt to close the hole by returning error messages from this reverse engineered API which direct the user to the official tool instead. On top of this, Xiaomi requires you to associate the phone to be unlocked with a user account on their services, paired to a phone number, and has a 30-day waiting period between unlocks. I ultimately had to resort to a Windows 10 VM with USB passthrough to get the damn thing unlocked. This is very frustrating and far from the spirit of free software; Xiaomi earns few points for openness in my books.&lt;/p&gt;&lt;p&gt;Once unlocked, the “initial setup bullshit” did not cease. The main issue is that the postmarketOS flashing tool (which is just a wrapper around fastboot) seemed to have problems writing a consistent filesystem. I was required to apply a level of Linux expertise which exceeds that of even most enthusiasts to obtain a shell in the initramfs, connect to it over postmarketOS’s telnet debugging feature, and run fsck.ext4 to fix the filesystem. Following this, I had to again apply a level of Alpine Linux expertise which exceeds that of many enthusiasts to repair installed packages and get everything up to a baseline of workitude. Overall, it took me the better part of a day to get to a baseline of “running a working installation of postmarketOS”.&lt;/p&gt;&lt;p&gt;However: following the “initial setup bullshit”, I found a very manageable scale of “ongoing problems”. The device’s base performance is excellent, far better than the PinePhone — it just performs much like I would expect from a normal phone. PostmarketOS is, as always, brilliant, and all of the usual mainline Alpine Linux trimmings I would expect are present — I can SSH in, I easily connected it to my personal VPN, and I’m able to run most of the software I’m already used to from desktop Linux systems (though, of course, GUI applications range widely in their ability to accomodate touch screens and a portrait mobile form-factor). I transferred my personal data over from my PinePhone using a method which is 100% certifiably absent of bullshit, namely just rsyncing over my home directory. Excellent!&lt;/p&gt;&lt;p&gt;Telephony support also works pretty well. Audio profiles are a bit buggy, and I can often find my phone using my headphone output while I don’t have them plugged in instead of the speakers, having to resort to manually switching between them from time to time. However, I have never had an issue with the audio profiles being wrong during a phone call (the modem works, by the way); earpiece and speakerphone both work as expected. That said, I have heard complaints from recipients of my phone calls about hearing an echo of their own voice. Additionally, DTMF tones do not work, but &lt;a href=&quot;https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/823&quot; target=&quot;_blank&quot;&gt;the fix&lt;/a&gt; has already been merged and is expected in the next release of ModemManager. SMS and mobile data work fine, and mobile data works with a lesser degree of bullshit than I was prepared to expect after reading the pmOS wiki page for this device.&lt;/p&gt;&lt;p&gt;Another problem is that the phone’s onboard cameras do not work at all, and it seems unlikely that this will be solved in the near future. This is not really an issue for me. Another papercut is that Phosh handles the display notch poorly, and though pmOS provides a “tweak” tool which can move the clock over from behind the notch, it leaves something to be desired. The &lt;a href=&quot;https://gitlab.gnome.org/World/Phosh/phosh/-/issues/552&quot; target=&quot;_blank&quot;&gt;relevant issue&lt;/a&gt; is being discused on the Phosh issue tracker and a fix is presumably coming soon — it doesn’t seem particularly difficult to solve. I have also noted that, though GPS works fine, Mepo &lt;a href=&quot;https://redacted.moe/f/40dd2e96.png&quot; target=&quot;_blank&quot;&gt;renders incorrectly&lt;/a&gt; and Gnome Maps has (less severe) &lt;a href=&quot;https://redacted.moe/f/ce6414d5.png&quot; target=&quot;_blank&quot;&gt;display issues&lt;/a&gt; as well.&lt;/p&gt;&lt;p&gt;The battery life is not as good as the PinePhone, which itself is not as good as most Android phones. However, it meets my needs. It seems to last anywhere from 8 to 10 hours depending on usage, following a full night’s charge. As such, I can leave it off of the juice when I go out without too much fear. That said, I do keep a battery bank in my backpack just in case, but that’s also just a generally useful thing to have around. I think I’ve lent it to others more than I’ve used it myself.&lt;/p&gt;&lt;p&gt;There are many other apps which work without issues. I found that &lt;a href=&quot;https://johnfactotum.github.io/foliate/&quot; target=&quot;_blank&quot;&gt;Foliate&lt;/a&gt; works great for reading e-books and &lt;a href=&quot;https://wiki.gnome.org/Apps/Evince&quot; target=&quot;_blank&quot;&gt;Evince&lt;/a&gt; works nicely for PDFs (two use-cases which one might perceive as related, but which I personally have different UI expectations for). Firefox has far better performance on this device than on the PinePhone and allows for very comfortable web browsing. I also discovered &lt;a href=&quot;https://gfeeds.gabmus.org/&quot; target=&quot;_blank&quot;&gt;Gnome Feeds&lt;/a&gt; which, while imperfect, accommodates my needs regarding an RSS feed reader. All of the “standard” mobile Linux apps that worked fine on the PinePhone also work fine here, such as Lollypop for music and the Porfolio file manager.&lt;/p&gt;&lt;p&gt;I was pleasantly surprised that, after enduring some more bullshit, I was able to get &lt;a href=&quot;https://waydro.id/&quot; target=&quot;_blank&quot;&gt;Waydroid&lt;/a&gt; to work, allowing me to run Android applications on this phone. My expectations for this were essentially non-existent, so any degree of workitude was a welcome surprise, and any degree of non-workitude was the expected result. On the whole, I’m rather impressed, but don’t expect anything near perfection. The most egregious issue is that I found that internal storage simply doesn’t work, so apps cannot store or read common files (though they seem to be able to persist their own private app data just fine). The camera does not work, so the use-case I was hoping to accommodate here — running my bank’s Android app — is not possible. However, I was able to install F-Droid and a small handful of Android apps that work with a level of performance which is indistinguishable from native Android performance. It’s not quite there yet, but Waydroid has a promising future and will do a lot to bridge the gap between Android and mainline Linux on mobile.&lt;/p&gt;&lt;p&gt;On the whole, I would rate the Poco F1’s bullshit level as follows:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Initial setup: miserable&lt;/li&gt;&lt;li&gt;Ongoing problems: minor&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I have a much higher tolerance for “initial setup” bullshit than for ongoing problems bullshit, so this is a promising result for my needs. I have found that this device is ahead of the PinePhone that I had been using previously in almost all respects, and I have switched to it as my daily driver. In fact, this phone, once the initial bullshit is addressed, is complete enough that it may be the first mainline Linux mobile experience that I might recommend to others as a daily driver. I’m glad that I made the switch.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/pmOS-on-xiaomi-poco-f1/</link>
        
        <pubDate>Thu, 25 Aug 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/pmOS-on-xiaomi-poco-f1/</guid>
      </item>
    
      <item>
        
        
          <title>PINE64 has let its community down</title>
          <description>
            &lt;p&gt;Context for this post:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2022/01/18/Pine64s-weird-priorities.html&quot; target=&quot;_blank&quot;&gt;Pine64 should re-evaluate their community priorities&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://tuxphones.com/pine-formula/&quot; target=&quot;_blank&quot;&gt;The Pine Formula&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://blog.brixit.nl/why-i-left-pine64/&quot; target=&quot;_blank&quot;&gt;Why I left PINE64&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.pine64.org/2022/08/18/a-response-to-martijns-blog/&quot; target=&quot;_blank&quot;&gt;A response to Martijn’s blog&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;p&gt;I know that apologising and taking responsibility for your mistakes is difficult. It seems especially difficult for commercial endeavours, which have fostered a culture of cold disassociation from responsibility for their actions, where admitting to wrongdoing is absolutely off the table. I disagree with this culture, but I understand where it comes from, and I can empathise with those who find themselves in the position of having to reconsider their actions in the light of the harm they have done. It’s not easy.&lt;/p&gt;&lt;p&gt;But, the reckoning must come. I have been a long-time supporter of PINE64. On this blog I have written positively about the &lt;a href=&quot;https://drewdevault.com/2019/12/18/PinePhone-review.html&quot; target=&quot;_blank&quot;&gt;PinePhone&lt;/a&gt; and &lt;a href=&quot;https://drewdevault.com/2021/05/14/Pinebook-Pro-review.html&quot; target=&quot;_blank&quot;&gt;PineBook Pro&lt;/a&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; I believed that PINE64 was doing the right thing and was offering something truly revolutionary on the path towards getting real FOSS systems into phones. I use a PinePhone as my daily driver,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and I also own a PineBook Pro, two RockPro64s, a PinePhone Pro, and a PineNote as well. All of these devices have issues, some of them crippling, but PINE64’s community model convinced me to buy these with confidence in the knowledge that they would be able to work with the community to address these flaws given time.&lt;/p&gt;&lt;p&gt;However, PINE64’s treatment of its community has been in a steady decline for the past year or two, culminating in postmarketOS developer Martijn Braam’s &lt;a href=&quot;https://blog.brixit.nl/why-i-left-pine64/&quot; target=&quot;_blank&quot;&gt;blog post&lt;/a&gt; outlining a stressful and frustrating community to participate in, a lack of respect from PINE64 towards this community, and a model moving from a diverse culture that builds working software together to a Manjaro mono-culture that doesn’t. PINE64 offered a &lt;a href=&quot;https://www.pine64.org/2022/08/18/a-response-to-martijns-blog/&quot; target=&quot;_blank&quot;&gt;disappointing response&lt;/a&gt;. In their blog post, they dismiss the problems Martijn brings up, paint his post as misguided at best and disingenuous at worst, and fail to take responsibility for their role in any of these problems.&lt;/p&gt;&lt;p&gt;The future of PINE64’s Manjaro mono-culture is dim. Manjaro is a very poorly run Linux distribution with a history of financial mismanagement, ethical violations, security incidents, shipping broken software, and disregarding the input of its peers in the distribution community. Just this morning they allowed their SSL certificates to expire — for the fourth time. An &lt;a href=&quot;https://dont-ship.it/&quot; target=&quot;_blank&quot;&gt;open letter&lt;/a&gt;, signed jointly by 16 members of the Linux mobile community, called out bad behaviors which are largely attributable to Manjaro. I do not respect their privileged position in the PINE64 community, which I do not expect to be constructive or in my best interests. I have never been interested in running Manjaro on a PINE64 device and once they turn their back on the lush ecosystem they promised, I no longer have any interest in the platform.&lt;/p&gt;&lt;p&gt;It’s time for PINE64 to take responsibility for these mistakes, and make clear plans to correct them. To be specific:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Apologise for mistreatment of community members.&lt;/li&gt;&lt;li&gt;Make a tangible commitment to honoring and respecting the community.&lt;/li&gt;&lt;li&gt;Rescind their singular commitment to Manjaro.&lt;/li&gt;&lt;li&gt;Re-instate community editions and expand the program.&lt;/li&gt;&lt;li&gt;Deal with this stupid SPI problem. The community is right, listen to them.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I understand that it’s difficult to acknowledge our mistakes. But it is also necessary, and important for the future of PINE64 and the future of mobile Linux in general. I call on TL Lim, Marek Kraus, and Lukasz Erecinski to personally answer for these problems.&lt;/p&gt;&lt;p&gt;There are three possible outcomes to this controversy, depending on PINE64’s response. If PINE64 refuses to change course, the community will continue to decay and fail — the community PINE64 depends on to make its devices functional and useful. Even the most mature PINE64 products still need a lot of work, and none of the new products are even remotely usable. This course of events will be the end of PINE64 and deal a terrible blow to the mobile FOSS movement.&lt;/p&gt;&lt;p&gt;The other option for PINE64 to change its behavior. They do this with grace, or without. If they crumble under public pressure and, for example, spitefully agree to re-instate community editions without accepting responsibility for their wrongdoings, it does not bode well for addressing the toxic environment which is festering in the PINE64 community. This may be better than the worst case, but may not be enough. New community members may hesitate to join, maligned members may not offer their forgiveness, and PINE64’s reputation will suffer for a long time.&lt;/p&gt;&lt;p&gt;The last option is for PINE64 to act with grace and humility. Acknowledge your mistakes and apologise to those who have been hurt. Re-commit to honoring your community and treating your peers with respect. Remember, the community are volunteers. They have no obligation to make peace, so it’s on you to mend these wounds. It will still be difficult to move forward, but doing it with humility, hand in hand with the community, will set PINE64 up with the best chance of success. We’re counting on you to do the right thing.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/PINE64-let-us-down/</link>
        
        <pubDate>Thu, 18 Aug 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/PINE64-let-us-down/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, August 2022</title>
          <description>
            &lt;p&gt;It is a blessedly cool morning here in Amsterdam. I was busy moving house earlier this month, so this update is a bit quieter than most.&lt;/p&gt;&lt;p&gt;For a fun off-beat project this month, I started working on a &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hdmg&quot; target=&quot;_blank&quot;&gt;GameBoy emulator&lt;/a&gt; written in Hare. No promises on when it will be functional or how much I plan on working on it – just doing it for fun. In more serious Hare news, I have implemented Thread-Local Storage (TLS) for qbe, our compiler backend. Hare’s standard library does not support multi-threading, but I needed this for Helios, whose driver library does support threads. It will also presumably be of use for cproc once it lands upstream.&lt;/p&gt;&lt;p&gt;Speaking of Helios, it received the runtime components for TLS support on x86_64, namely the handling of %fs and its base register MSR in the context switch, and updates to the ELF loader for handling .tdata/.tbss sections. I have also implemented support for moving and copying capabilities, which will be useful for creating new processes in userspace. Significant progress towards capability destructors was also made, with some capabilities — pages and page tables in particular — being reclaimable now. Next goal is to finish up all of this capability work so that you can freely create, copy, move, and destroy capabilities, then use all of these features to implement a simple shell. There is also some refactoring due at some point soon, so we’ll see about that.&lt;/p&gt;&lt;p&gt;Other Hare progress has been slow this month, as I’m currently looking at a patch queue 123 emails backed up. When I’m able to sit down and get through these, we can expect a bunch of updates in short order.&lt;/p&gt;&lt;p&gt;SourceHut news will be covered in the “what’s cooking” post later today. That’s all for now! Thanks for tuning in.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-August-2022/</link>
        
        <pubDate>Tue, 16 Aug 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-August-2022/</guid>
      </item>
    
      <item>
        
        
          <title>How I wish I could organize my thoughts</title>
          <description>
            &lt;p&gt;I keep a pen &amp; notebook on my desk, which I make liberal use of to jot down my thoughts. It works pretty well: ad-hoc todo lists, notes on problems I’m working on, tables, flowcharts, etc. It has some limitations, though. Sharing anything out of my notebook online is an awful pain in the ass. I can’t draw a straight line to save my life, so tables and flowcharts are a challenge. No edits, either, so lots of crossed-out words and redrawn or rewritten pages. And of course, my handwriting sucks and I can type much more efficiently than I can write. I wish this was a digital medium, but there are not any applications available which can support the note-taking paradigm that I wish I could have. What would that look like?&lt;/p&gt;&lt;p&gt;Well, like this (click for full size):&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://redacted.moe/f/920d5d58.png&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/920d5d58.png&quot;&gt;
&lt;figcaption&gt;A mock-up of an application. A4 pages are arranged ad-hoc on a grid. Handwritten notes and drawings appear in red across the grid and over the pages. A flowchart is shown outside of a page.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I don’t have the bandwidth to take on a new project of this scope, so I’ll describe what I think this should look like in the hopes that it will inspire another team to work on something like this. Who knows!&lt;/p&gt;&lt;p&gt;The essential interface would be an infinite grid on which various kinds of objects can be placed by the user. The most important of these objects would be pages, at a page size configurable by the user (A4 by default). You can zoom in on a page (double click it or something) to make it your main focus, zooming in automatically to an appropriate level for editing, then type away. A simple WYSIWYG paradigm would be supported here, perhaps supporting only headings, bold/italic text, and ordered and unordered lists — enough to express your thoughts but not a full blown document editor/typesetter.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; When you run out of page, another is generated next to the current page, either to the right or below — configurable.&lt;/p&gt;&lt;p&gt;Other objects would include flowcharts, tables, images, hand-written text and drawings, and so on. These objects can be placed free form on the grid, or embedded in a page, or moved between each mode.&lt;/p&gt;&lt;p&gt;The user input paradigm should embrace as many modes of input as the user wants to provide. Mouse and keyboard: middle click to pan, scroll to zoom in or out, left click and drag to move objects around, shift+click to select objects, etc. A multi-point trackpad should support pinch to zoom, two finger pan, etc. Touch support is fairly obvious. &lt;a href=&quot;https://en.wikipedia.org/wiki/Graphics_tablet&quot; target=&quot;_blank&quot;&gt;Drawing tablet&lt;/a&gt; support is also important: the user should be able to use one to draw and write free-form. I’d love to be able to make flowcharts by drawing boxes and arrows and having the software recognize them and align them to the grid as first-class vector objects. Some drawing tablets support trackpad and touch-screen-like features as well — so all of those interaction options should just werk.&lt;/p&gt;&lt;p&gt;Performance is important here. I should be able to zoom in and out and pan around while all of the objects rasterize themselves in real-time, never making the user suffer through stuttery interactions. There should also be various ways to export this content. A PDF exporter should let me arrange the pages in the desired linear order. SVG exporters should be able to export objects like flowcharts and diagrams. Other potential features includes real-time collaboration or separate templates for presentations.&lt;/p&gt;&lt;p&gt;Naturally this application should be free software and should run on Linux. However, I would be willing to pay a premium price for this tool — a one-time fee of as much as $1000, or subscriptions on the order of $100/month if real-time collaboration or cloud synchronization are included. If you’d like some ideas for how to monetize free software projects like this, feel free to swing by &lt;a href=&quot;https://hackmeeting.org/hackit22/schedule.html#talk-a2eb7aa1-90ac-48b9-8ac9-b16235eb2daf&quot; target=&quot;_blank&quot;&gt;my talk on the subject&lt;/a&gt; in Italy early this September to talk about it.&lt;/p&gt;&lt;p&gt;Well, that’s enough dreaming for now. I hope this inspired you, and in the meantime it’s back to pen and paper for me.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Organizing-my-thoughts/</link>
        
        <pubDate>Wed, 10 Aug 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Organizing-my-thoughts/</guid>
      </item>
    
      <item>
        
        
          <title>Conciseness</title>
          <description>
            &lt;p&gt;Conciseness is often considered a virtue among hackers and software engineers. FOSS maintainers in particular generally prefer to keep bug reports, questions on mailing lists, discussions in IRC channels, and so on, close to the point and with minimal faff. It’s not considered impolite to skip the formalities — quite the opposite. So: keep your faffery to a minimum. A quick “thanks!” at the end of a discussion will generally suffice. And, when someone is being direct with you, don’t interpret it as a slight: simply indulge in the blissful freedom of a discussion absent of faffery.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Conciseness/</link>
        
        <pubDate>Tue, 26 Jul 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Conciseness/</guid>
      </item>
    
      <item>
        
        
          <title>The past and future of open hardware</title>
          <description>
            &lt;p&gt;They say a sucker is born every day, and at least on the day of my birth, that certainly may have been true. I have a bad habit of spending money on open hardware projects that ultimately become vaporware or seriously under-deliver on their expectations. In my ledger are EOMA68, DragonBox Pyra, the Jolla Tablet — which always had significant non-free components — and the Mudita Pure, though I did successfully receive a refund for the latter two.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;There are some success stories, though. My Pine64 devices work great — though they have non-free components — and I have a HiFive Unmatched that I’m reasonably pleased with. Raspberry Pi is going well, if you can find one — also with non-free components — and Arduino and products like it are serving their niche pretty well. I hear the MNT Reform went well, though by then I had learned to be a bit more hesitant to open my wallet for open hardware, so I don’t have one myself. Pebble worked, until it didn’t. Caveats abound in all of these projects.&lt;/p&gt;&lt;p&gt;What does open hardware need to succeed, and why have many projects failed? And why do the successful products often have non-free components and poor stock? We can’t blame it all on the chip shortage and/or COVID: it’s been an issue for a long time.&lt;/p&gt;&lt;p&gt;I don’t know the answers, but I hope we start seeing improvements. I hope that the successful projects will step into a mentorship role to provide up-and-comers with tips on how they made their projects work, and that we see a stronger focus on liberating non-free components. Perhaps Crowd Supply can do some work in helping to secure investment&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; for open hardware projects, and continue the good work they’re already doing on guiding them through the development and production processes.&lt;/p&gt;&lt;p&gt;Part of this responsibility comes down to the consumer: spend your money on free projects, and don’t spend your money on non-free projects. But, we also need to look closely at the viability of each project, and open hardware projects need to be transparent about their plans, lest we get burned again. Steering the open hardware movement out of infancy will be a challenge for all involved.&lt;/p&gt;&lt;p&gt;Are you working on a cool open hardware project? &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Let me know&lt;/a&gt;. Explain how you plan on making it succeed and, if I’m convinced that your idea has promise, I’ll add a link here.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Open-hardware-graveyard/</link>
        
        <pubDate>Mon, 25 Jul 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Open-hardware-graveyard/</guid>
      </item>
    
      <item>
        
        
          <title>Code review at the speed of email</title>
          <description>
            &lt;p&gt;I’m a big proponent of the email workflow for patch submission and code review. I have previously published some content (&lt;a href=&quot;https://spacepub.space/w/no6jnhHeUrt2E5ST168tRL&quot; target=&quot;_blank&quot;&gt;How to use git.sr.ht’s send-email feature&lt;/a&gt;, &lt;a href=&quot;https://spacepub.space/w/3JhBcvEYbminv8ji4k84gx&quot; target=&quot;_blank&quot;&gt;Forks &amp; pull requests vs email&lt;/a&gt;, &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;git-send-email.io&lt;/a&gt;) which demonstrates the contributor side of this workflow, but it’s nice to illustrate the advantages of the maintainer workflow as well. For this purpose, I’ve recorded a short video demonstrating how I manage code review as an email-oriented maintainer.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Disclaimer: I am the founder of &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut&lt;/a&gt;, a platform built on this workflow which competes with platforms like GitHub and GitLab. This article’s perspective is biased.&lt;/em&gt;&lt;/p&gt;&lt;iframe title=&quot;Code review at the speed of email&quot; src=&quot;https://spacepub.space/videos/embed/385c414a-2bdc-4bf3-82a0-76fcb15093e9&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;

&lt;p class=&quot;text-center&quot;&gt;
  &lt;em&gt;
  This blog post provides additional material to supplement this video, and also
  includes all of the information from the video itself. For those who prefer
  reading over watching, you can just stick to reading this blog post. Or, you
  can watch the video and skim the post. Or you can just do something else! When
  was the last time you called your grandmother?
  &lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;With hundreds of hours of review experience on GitHub, GitLab, and SourceHut, I can say with confidence the email workflow allows me to work much faster than any of the others. I can review small patches in seconds, work quickly with multiple git repositories, easily test changes and make tweaks as necessary, rebase often, and quickly chop up and provide feedback for larger patches. Working my way through a 50-email patch queue usually takes me about 20 minutes, compared to an hour or more for the same number of merge requests.&lt;/p&gt;&lt;p&gt;This workflow also works entirely offline. I can read and apply changes locally, and even reply with feedback or to thank contributors for their patch. My mail setup automatically downloads mail from IMAP using &lt;a href=&quot;https://isync.sourceforge.io/&quot; target=&quot;_blank&quot;&gt;isync&lt;/a&gt; and outgoing mails are queued with &lt;a href=&quot;http://www.postfix.org/&quot; target=&quot;_blank&quot;&gt;postfix&lt;/a&gt; until the network is ready. I have often worked through my patch queue on an airplane or a train with spotty or non-functional internet access without skipping a beat. Working from low-end devices like a Pinebook or a phone are also no problem — aerc is very lightweight in the terminal and the SourceHut web interface is &lt;a href=&quot;https://forgeperf.org&quot; target=&quot;_blank&quot;&gt;much lighter &amp; faster&lt;/a&gt; than any other web forge.&lt;/p&gt;&lt;p&gt;The centerpiece of my setup is an email client I wrote specifically for software development using this workflow: &lt;a href=&quot;https://aerc-mail.org/&quot; target=&quot;_blank&quot;&gt;aerc&lt;/a&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; The stock configuration of aerc is pretty good, but I make a couple of useful additions specifically for development on SourceHut. Specifically, I add a few keybindings to &lt;code&gt;~/.config/aerc/binds.conf&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[messages]
ga = :flag&amp;lt;Enter&amp;gt;:pipe -mb git am -3&amp;lt;Enter&amp;gt;
gp = :term git push&amp;lt;Enter&amp;gt;
gl = :term git log&amp;lt;Enter&amp;gt;

rt = :reply -a -Tthanks&amp;lt;Enter&amp;gt;
Rt = :reply -qa -Tquoted_thanks&amp;lt;Enter&amp;gt;

[compose::review]
V = :header -f X-Sourcehut-Patchset-Update NEEDS_REVISION&amp;lt;Enter&amp;gt;
A = :header -f X-Sourcehut-Patchset-Update APPLIED&amp;lt;Enter&amp;gt;
R = :header -f X-Sourcehut-Patchset-Update REJECTED&amp;lt;Enter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first three commands, ga, gp, and gl, are for invoking git commands. “ga” applies the current email as a patch, using &lt;a href=&quot;https://git-scm.com/docs/git-am&quot; target=&quot;_blank&quot;&gt;git am&lt;/a&gt;, and “gp” simply runs git push. “gl” is useful for quickly reviewing the git log. ga also flags the email so that it shows up in the UI as having been applied, which is useful as I’m jumping all over a patch queue. I also make liberal use of \ (:filter) to filter my messages to patches applicable to specific projects or goals.&lt;/p&gt;&lt;p&gt;rt and Rt use aerc templates installed at &lt;code&gt;~/.config/aerc/templates/&lt;/code&gt; to reply to emails after I’ve finished reviewing them. The “thanks” template is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;X-Sourcehut-Patchset-Update: APPLIED

Thanks!

{{exec &amp;quot;{ git remote get-url --push origin; git reflog -2 origin/master --pretty=format:%h | xargs printf &amp;apos;%s\n&amp;apos; | tac; } | xargs printf &amp;apos;To %s\n   %s..%s  master -&amp;gt; master&amp;apos;&amp;quot; &amp;quot;&amp;quot;}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And quoted_thanks is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;X-Sourcehut-Patchset-Update: APPLIED

Thanks!

{{exec &amp;quot;{ git remote get-url --push origin; git reflog -2 origin/master --pretty=format:%h | xargs printf &amp;apos;%s\n&amp;apos; | tac; } | xargs printf &amp;apos;To %s\n   %s..%s  master -&amp;gt; master&amp;apos;&amp;quot; &amp;quot;&amp;quot;}}

On {{dateFormat (.OriginalDate | toLocal) &amp;quot;Mon Jan 2, 2006 at 3:04 PM MST&amp;quot;}}, {{(index .OriginalFrom 0).Name}} wrote:
{{wrapText .OriginalText 72 | quote}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Both of these add a magic “X-Sourcehut-Patchset-Update” header, which updates the status of the patch on the mailing list. They also include a shell pipeline which adds some information about the last push from this repository, to help the recipient understand what happened to their patch. I often make some small edits to request the user follow-up with a ticket for some future work, or add other timely comments. The second template, quoted_reply, is also particularly useful for this: it quotes the original message so I can reply to specific parts of it, in the commit message, timely commentary, or the code itself, often pointing out parts of the code that I made some small tweaks to before applying.&lt;/p&gt;&lt;p&gt;And that’s basically it! You can browse all of my dotfiles &lt;a href=&quot;https://git.sr.ht/~sircmpwn/dotfiles&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; to see more details about my system configuration. With this setup I am able to work my way through a patch queue easier and faster than ever before. That’s why I like the email workflow so much: for power users, no alternative is even close in terms of efficiency.&lt;/p&gt;&lt;p&gt;Of course, this is the power user workflow, and it can be intimidating to learn all of these things. This is why we offer more novice-friendly tools, which lose some of the advantages but are often more intuitive. For instance, we are working on user interface on the web for patch review, mirroring our existing &lt;a href=&quot;https://spacepub.space/w/no6jnhHeUrt2E5ST168tRL&quot; target=&quot;_blank&quot;&gt;web interface for patch submission&lt;/a&gt;. But, in my opinion, it doesn’t get better than this for serious FOSS maintainers.&lt;/p&gt;&lt;p&gt;Feel free to reach out on IRC in #sr.ht.watercooler on Libera Chat, or &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;via email&lt;/a&gt;, if you have any questions about this workflow and how you can apply it to your own projects. Happy hacking!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Code-review-with-aerc/</link>
        
        <pubDate>Mon, 25 Jul 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Code-review-with-aerc/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, July 2022</title>
          <description>
            &lt;p&gt;Hello there! It’s been a hot July week in Amsterdam, and I expect hotter days are still to come. I wish air conditioning was more popular in Europe, but alas. This month of FOSS development enjoyed a lot of small improvements in a lot of different projects.&lt;/p&gt;&lt;p&gt;For Hare, I have introduced a number of improvements. I wrote a new standard library module for string templates, &lt;a href=&quot;https://docs.harelang.org/strings/template&quot; target=&quot;_blank&quot;&gt;strings::template&lt;/a&gt;, and a new third-party library for working with pixel buffers, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/pixbuf&quot; target=&quot;_blank&quot;&gt;pixbuf&lt;/a&gt;. The templating is pretty simple — as is typical for the standard library — but allows a fairly wide range of formatting options. We’ll be extending this a little bit more in the future, but it will not be a complete solution like you see in things like Jinja2. Nevertheless, it makes some use-cases, like code generation, a lot cleaner, without introducing a weighty or complex dependency.&lt;/p&gt;&lt;p&gt;pixbuf is pretty neat, and is the first in a line of work I have planned for graphics on Hare. It’s similar to pixman, but with a much smaller scope — it only deals with pixel buffers, handling pixel format conversions and doing small operations like fill and copy. In the future I will add simple buffer compositing as well, and extending modules like &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare-png&quot; target=&quot;_blank&quot;&gt;hare-png&lt;/a&gt; to support loading data into these buffers. Later, I plan on writing a simple vector graphics library, capable at least of rendering &lt;a href=&quot;https://tinyvg.tech&quot; target=&quot;_blank&quot;&gt;TinyVG&lt;/a&gt; images and perhaps later TinySVG as well. I’m also working on &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare-wayland&quot; target=&quot;_blank&quot;&gt;hare-wayland&lt;/a&gt; again, to provide a place to display these buffers.&lt;/p&gt;&lt;p&gt;I also introduced &lt;a href=&quot;https://docs.harelang.org/format/tar&quot; target=&quot;_blank&quot;&gt;format::tar&lt;/a&gt;, which will serve as the basis of initramfs-alike functionality for Helios. On the subject of Helios, much work has been completed. I have implemented a PCI driver and a small proof-of-concept AHCI driver (for reading from SATA disks). Alexey Yerin has also been hard at work on the RISC-V port, and has successfully implemented an e1000 ethernet driver which can send and receive ICMP (ping) packets. I also completed IRQ control for userspace, so that userspace device drivers can process interrupts, and used it to write a keyboard driver for a functional &lt;a href=&quot;https://drewdevault.com/2022/07/01/Porting-DOOM-to-Helios.html&quot; target=&quot;_blank&quot;&gt;DOOM port&lt;/a&gt;. The full DOOM port required a fair bit of work — check out that blog post for the complete details. The idle thread was also added, so that all processes can be blocked waiting on interrupts, signals, endpoints, etc. Non-blocking send, receive, and wait syscalls were also added this month.&lt;/p&gt;&lt;p&gt;I’m working on splitting memory capabilities into separate device- and general-purpose capabilities, then adding support for destroying capabilities when they’re no longer required. I also implemented pre-emptive multi-tasking early this month, and the vulcan test suite now has several multi-threaded tests to verify IPC functionality. However, a couple of pieces are missing — the ability to create and work with new cspaces and vspaces — in order to spawn new processes. I’ll be focusing on these tasks in the coming weeks. With these pieces in place, we can start working on Mercury and Vulcan: the driver system.&lt;/p&gt;&lt;p&gt;I’ll save the SourceHut news for the “what’s cooking” post later today, so that’s all for now. Until next time!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-July-2022/</link>
        
        <pubDate>Mon, 18 Jul 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-July-2022/</guid>
      </item>
    
      <item>
        
        
          <title>The Fediverse can be pretty toxic</title>
          <description>
            &lt;p&gt;Mastodon, inspired by GNU social, together with Pleroma, form the most popular components of what we know as the “Fediverse” today. All of them are, in essence, federated, free software Twitter clones, interoperable with each other via the ActivityPub protocol.&lt;/p&gt;&lt;p&gt;In many respects, the Fediverse is a liberating force for good. Its federated design distributes governance and costs across many independent entities, something I view as a very strong design choice. Its moderation tools also do a pretty good job of keeping neo-nazis out of your feeds and providing a comfortable space to express yourself in, especially if your form of expression is maligned by society. Large groups of Fediverse members have found in it a home for self-expression which is denied to them elsewhere on the basis of their sexuality, gender expression, politics, or other characteristics. It’s also essentially entirely free from commercial propaganda.&lt;/p&gt;&lt;p&gt;But it’s still just a Twitter clone, and many of the social and psychological ills which come with that are present in the Fediverse. It’s a feed of other people’s random thoughts, often unfiltered, presented to you without value judgement — even when a value judgement may be wise. Features like boosting and liking posts, chasing after follower counts and mini-influencers, these rig up dopamine reinforcement like any other social network does. The increased character limit does not really help; most posts are pretty short and no one wants to read an essay aggressively word-wrapped in a narrow column.&lt;/p&gt;&lt;p&gt;The Fediverse is an environment optimized for flame wars. Arguments in this medium are held under these constraints, in public, with the peanut gallery of followers from either side stepping in and out to reinforce their position and flame the opponents. Progress is measured in gains of ideological territory and in the rising and falling swells of participants dotting their comments throughout huge threads. You are not just arguing your position, but performing it to your audience, and to your opponent’s audience.&lt;/p&gt;&lt;p&gt;Social networks are not good for you. The Fediverse brought out the worst in me, and it can bring out the worst in you, too. The behaviors it encourages are plainly defined as harassment, a behavior which is not unique to any ideological condition. People get hurt on the Fediverse. Keep that in mind. Consider taking a look in the mirror and asking yourself if your relationship with the platform is healthy for you and for the people around you.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Fediverse-toxicity/</link>
        
        <pubDate>Sat, 09 Jul 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Fediverse-toxicity/</guid>
      </item>
    
      <item>
        
        
          <title>Porting Doom to Helios</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Doom_(1993_video_game)&quot; target=&quot;_blank&quot;&gt;Doom&lt;/a&gt; was an incredibly popular video game by Id software which, six years following its release, was made &lt;a href=&quot;https://github.com/id-Software/DOOM&quot; target=&quot;_blank&quot;&gt;open source&lt;/a&gt; under the GPLv2 license. Thanks to this release, combined with the solid software design and lasting legacy of backwards compatibility in C, Doom has been ported to countless platforms by countless programmers. And I recently added myself to this number :)&lt;/p&gt;&lt;p&gt;&lt;video src=&quot;https://redacted.moe/f/588e2fbd.webm&quot; controls muted autoplay&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;I’m working on a new kernel called &lt;a href=&quot;https://sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Helios&lt;/a&gt;, and I thought that porting Doom would present a good opportunity for proving the kernel design — you never know if you have a good design until you try to use it for real. Doom is a good target because it does not require much to get working, but it is a useful (and fun) program to port. It calls for the following features:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;A working C programming environment&lt;/li&gt;&lt;li&gt;Dynamic memory allocation&lt;/li&gt;&lt;li&gt;A place to draw the screen (a framebuffer)&lt;/li&gt;&lt;li&gt;Keyboard input&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;As I was working, I gradually came to understand that Helios was pretty close to supporting all of these features, and thought that the time to give Doom a shot was coming soon. In my &lt;a href=&quot;https://drewdevault.com/2022/06/15/Status-update-June-2022.html&quot; target=&quot;_blank&quot;&gt;last status update&lt;/a&gt;, I shared a picture of a Helios userspace program utilizing the framebuffer provided by multiboot, ticking one box. We’ve had dynamic memory allocation in userspace working since June 8th. The last pieces were a keyboard driver and a C library.&lt;/p&gt;&lt;p&gt;I started with the keyboard driver, since that would let me continue to work on Hare for a little bit longer, providing a more direct benefit to the long-term goals (rather than the short-term goal of “get Doom to work”). Since Helios is a micro-kernel, the keyboard driver is implemented in userspace. A PS/2 keyboard driver requires two features which are reserved to ring 0: I/O ports and IRQ handling. To simplify the interface to the essentials for this use-case, pressing or releasing a key causes IRQ 1 to be fired on the PIC, then reading from port 0x60 provides a scancode. We already had support for working with I/O ports in userspace, so the blocker here was IRQ handling.&lt;/p&gt;&lt;p&gt;Helios implements IRQs similarly to seL4, by using a “notification” object (an IPC primitive) which is signalled by the kernel when an IRQ occurs. I was pleased to have this particular blocker, as developing out our IPC implementation further was a welcome task. The essential usage of a notification involves two operations: wait and signal. The former blocks until the notification is signalled, and the later signals the notification and unblocks any tasks which are waiting on it. Unlike sending messages to endpoints, signal never blocks.&lt;/p&gt;&lt;p&gt;After putting these pieces together, I was able to write a simple PS/2 keyboard driver which echos pressed keys to the kernel console:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq1_notify&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;newnotification&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq1&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irqcontrol_issue&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;INIT_CAP_IRQCONTROL&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq1_notify&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ps2&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;iocontrol_issue&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;INIT_CAP_IOCONTROL&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0x60&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0x64&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq1_notify&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;scancode&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioport_in8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ps2&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0x60&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;helios&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;irq_ack&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;irq1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a notification capability to wait on IRQs, then creates a capability for IRQ 1 registered for that notification. It also issues an I/O port capability for the PS/2 ports, 0x60-0x64 (inclusive). Then it loops, waiting until an interrupt occurs, reading the scancode from the port, and printing it. Simple!&lt;/p&gt;&lt;p&gt;I now turned my attention to a C library for Doom. The first step for writing userspace programs in C for a new operating system is to produce a suitable C cross-compiler toolchain. I adapted the instructions from &lt;a href=&quot;https://wiki.osdev.org/GCC_Cross-Compiler&quot; target=&quot;_blank&quot;&gt;this OSdev wiki tutorial&lt;/a&gt; for my needs and produced the working patches for &lt;a href=&quot;https://git.sr.ht/~sircmpwn/binutils/commit/b104dee8b4d5f6fb57d585132775e22f0eba80df&quot; target=&quot;_blank&quot;&gt;binutils&lt;/a&gt; and &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gcc/commit/20df2b4d99670f2db51f84dc57d2253fd71d0b2b&quot; target=&quot;_blank&quot;&gt;gcc&lt;/a&gt;. I started on a simple C library that included &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mercury/tree/f80bb66373ab12a66a9a86894d212cbbdfcf53bf/item/libc/syscall.s&quot; target=&quot;_blank&quot;&gt;some assembly glue&lt;/a&gt; for syscalls, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mercury/tree/f80bb66373ab12a66a9a86894d212cbbdfcf53bf/item/libc/crt&quot; target=&quot;_blank&quot;&gt;an entry point&lt;/a&gt;, and &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mercury/tree/1217b54b7bd09bebcd672d1e9cdae14f2e2e545f/item/libc/syscalls.c&quot; target=&quot;_blank&quot;&gt;a couple of syscall wrappers&lt;/a&gt;. With great anticipation, I wrote the following C program and loaded it into Helios:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;helios/syscall.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;main&lt;/span&gt;() {
	&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Hello from userspace in C!\n&amp;quot;&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;sys_writecons&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;message&lt;/span&gt;, &lt;span class=&quot;constant variable function&quot;&gt;strlen&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;message&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;$ qemu-system-x86_64 -m 1G -no-reboot -no-shutdown \
	-drive file=boot.iso,format=raw \
	-display none \
	-chardev stdio,id=char0 \
	-serial chardev:char0
Booting Helios kernel
Hello from userspace in C!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Woohoo! After a little bit more work setting up the basics, I started rigging &lt;a href=&quot;https://github.com/ozkl/doomgeneric&quot; target=&quot;_blank&quot;&gt;doomgeneric&lt;/a&gt; (a Doom fork designed to be easy to port) up to my cross environment and seeing what would break.&lt;/p&gt;&lt;p&gt;As it turned out, a lot of stuff would break. doomgeneric is designed to be portable, but it actually depends on a lot of stuff to be available from the C environment: stdio, libmath, string.h stuff, etc. Not too much, but more than I cared to write from scratch. So, I started pulling in large swaths of &lt;a href=&quot;https://musl.libc.org&quot; target=&quot;_blank&quot;&gt;musl libc&lt;/a&gt;, trimming out as much as I could, and wriggling it into a buildable state. I also wrote a lot of shims to fake out having a real Unix system to run it in, like this code for defining stdout &amp; stderr to just write to the kernel console:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;writecons&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;) {
	&lt;span class=&quot;constant variable function&quot;&gt;sys_writecons&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;wbase&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;wpos&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;wbase&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;sys_writecons&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;wend&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;buf_size&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;wpos&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;wbase&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;keyword&quot;&gt;#undef&lt;/span&gt; stdout
&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;stdoutbuf&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;BUFSIZ&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;UNGET&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__stdout_FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; {
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;stdoutbuf&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;UNGET&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;buf_size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;stdoutbuf&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;UNGET&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;F_PERM&lt;/span&gt; | &lt;span class=&quot;constant variable&quot;&gt;F_NORD&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;lbf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;&amp;apos;\n&amp;apos;&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;writecons&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;seek&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;close&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;,
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;stdout&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;__stdout_FILE&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__stdout_used&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;__stdout_FILE&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;#undef&lt;/span&gt; stderr
&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;stderrbuf&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;UNGET&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__stderr_FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; {
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;stderrbuf&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;UNGET&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;buf_size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;F_PERM&lt;/span&gt; | &lt;span class=&quot;constant variable&quot;&gt;F_NORD&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;lbf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;writecons&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;seek&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;close&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt;,
	&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;,
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;stderr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;__stderr_FILE&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__stderr_used&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;__stderr_FILE&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of all of this hacking and slashing is quite a mess, and none of this is likely to be useful in the long term. I did this work over the course of a couple of afternoons just to get everything “working” enough to support Doom, but an actual useful C programming environment for Helios is likely some ways off. Much of the near-term work will be in Mercury, which will be a Hare environment for writing drivers, and we won’t see a serious look at better C support until we get to Luna, the POSIX compatibility layer a few milestones away.&lt;/p&gt;&lt;p&gt;Anyway, in addition to pulling in lots of musl libc, I had to write some original code to create C implementations of the userspace end for working with Helios kernel services. Some of this is pretty straightforward, such as the equivalent of the helios::ioport_issue code from the keyboard driver you saw earlier:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;iocontrol_issue&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ctrl&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;min&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;max&lt;/span&gt;)
{
	&lt;span class=&quot;type&quot;&gt;uint64_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;mktag&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;IO_ISSUE&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cap&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;capalloc&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;ipc_buffer&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;caddr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;sysret&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;sys_send&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;ctrl&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;tag&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;min&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;max&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A more complex example is the code which maps a page of physical memory into the current process’s virtual address space. In Helios, similar to L4, userspace must allocate its own page tables. However, these page tables are semantically &lt;em&gt;owned&lt;/em&gt; by userspace, but they’re not actually &lt;em&gt;reachable&lt;/em&gt; by userspace — the page tables themselves are not mapped into their address space (for obvious reasons, I hope). A consequence of this is that the user cannot examine the page tables to determine which, if any, intermediate page tables have to be allocated in order to perform a desired memory mapping. The solution is to try the mapping anyway, and if the page tables are missing, the kernel will reply telling you which table it needs to complete the mapping request. You allocate the appropriate table and try again.&lt;/p&gt;&lt;p&gt;Some of this workload falls on userspace. I had already done this part in Hare, but I had to revisit it in C:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;sysret&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;page_map&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vspace&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;uintptr_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;)
{
	&lt;span class=&quot;type&quot;&gt;uint64_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;mktag&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;PAGE_MAP&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;ipc_buffer&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;caps&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vspace&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;sys_send&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;tag&lt;/span&gt;, (&lt;span class=&quot;type&quot;&gt;uint64_t&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;map_table&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;uintptr_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pt_type&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kind&lt;/span&gt;)
{
	&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;kind&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_PDPT&lt;/span&gt;:
		&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;retype&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;table&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;CT_PDPT&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_PD&lt;/span&gt;:
		&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;retype&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;table&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;CT_PD&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;PT_PT&lt;/span&gt;:
		&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;retype&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;table&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;CT_PT&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;default&lt;/span&gt;:
		&lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;sysret&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;page_map&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;table&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;INIT_CAP_VSPACE&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;MISSING_TABLES&lt;/span&gt;) {
		&lt;span class=&quot;constant variable function&quot;&gt;map_table&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;value&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable function&quot;&gt;map_table&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;kind&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
}

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;map&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;uintptr_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;) {
		&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;sysret&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;page_map&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;INIT_CAP_VSPACE&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;MISSING_TABLES&lt;/span&gt;) {
			&lt;span class=&quot;constant variable function&quot;&gt;map_table&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;value&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		} &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt; {
			&lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		}
	}
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;vaddr&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Based on this work, I was able to implement a very stupid malloc, which rounds all allocations up to 4096 and never frees them. Hey! It works, okay?&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uintptr_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x8000000000&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;page_alloc&lt;/span&gt;()
{
	&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;retype&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;CT_PAGE&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;malloc&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; % &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
		&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; % &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;type&quot;&gt;uintptr_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
		&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;page_alloc&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable function&quot;&gt;map&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;page&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is also &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mercury/tree/f80bb66373ab12a66a9a86894d212cbbdfcf53bf/item/libc/helios/device.c&quot; target=&quot;_blank&quot;&gt;devmap&lt;/a&gt;, which you can read in your own time, which is used for mapping device memory into your address space. This is neccessary to map the framebuffer. It’s more complex because it has to allocate a &lt;em&gt;specific&lt;/em&gt; physical page address into userspace, rather than whatever page happens to be free.&lt;/p&gt;&lt;p&gt;So, to revisit our progress, we have:&lt;/p&gt;&lt;p&gt;✓ A working C programming environment&lt;/p&gt;&lt;p&gt;✓ Dynamic memory allocation&lt;/p&gt;&lt;p&gt;✓ A place to draw the screen (a framebuffer)&lt;/p&gt;&lt;p&gt;✓ Keyboard input&lt;/p&gt;&lt;p&gt;It’s time for Doom, baby. Doomgeneric expects the porter to implement the following functions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;DG_Init&lt;/li&gt;&lt;li&gt;DG_DrawFrame&lt;/li&gt;&lt;li&gt;DG_GetKey&lt;/li&gt;&lt;li&gt;DG_SetWindowTitle&lt;/li&gt;&lt;li&gt;DG_SleepMs&lt;/li&gt;&lt;li&gt;DG_GetTicksMs&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Easy peasy. Uh, except for that last one. I forgot that our requirements list should have included a means of sleeping for a specific period of time. Hopefully that won’t be a problem later.&lt;/p&gt;&lt;p&gt;I started with DG_Init, allocating the pieces that we’ll need and stashing the important bits in some globals.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fb_width&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;fb_height&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;fb_pitch&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fb&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;irq1_notify&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;irq1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;cap_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ps2&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;DG_Init&lt;/span&gt;()
{
	&lt;span class=&quot;type&quot;&gt;uintptr_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vbeaddr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bootinfo&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;vbe_mode_info&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;uintptr_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vbepage&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vbeaddr&lt;/span&gt; / &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;vbe_mode_info&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vbe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;devmap&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;vbepage&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;vbeaddr&lt;/span&gt; % &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;fb_width&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vbe&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;fb_height&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vbe&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;fb_pitch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vbe&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;pitch&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;vbe&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;bpp&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;32&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;npage&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;vbe&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;pitch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vbe&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;height&lt;/span&gt;) / &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;fb&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;devmap&lt;/span&gt;((&lt;span class=&quot;type&quot;&gt;uintptr_t&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;vbe&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;framebuffer&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;npage&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;irq1_notify&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;mknotification&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;irq1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;irqcontrol_issue&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;INIT_CAP_IRQCONTROL&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;irq1_notify&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;ps2&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;iocontrol_issue&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;INIT_CAP_IOCONTROL&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0x60&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0x64&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the multiboot loader is configured to set up a framebuffer, it gets handed off to the kernel, and Helios provides it to userspace as mappable device memory, so that saves us from doing all of the annoying VBE crap (or heaven forbid, write an actual video driver). This lets us map the framebuffer into our process. Second, we do the same notification+IRQ+IOControl thing we did from the keyboard driver you saw earlier, except in C, so that we can process scancodes later.&lt;/p&gt;&lt;p&gt;Next is DG_DrawFrame, which is pretty straightforward. We just copy scanlines from the internal buffer to the framebuffer whenever it asks us to.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;DG_DrawFrame&lt;/span&gt;()
{
  &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;DOOMGENERIC_RESY&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;) {
    &lt;span class=&quot;constant variable function&quot;&gt;memcpy&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;fb&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fb_pitch&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;DG_ScreenBuffer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;DOOMGENERIC_RESX&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;DOOMGENERIC_RESX&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we have DG_GetKey, similar to our earlier keyboard driver, plus actually interpeting the scancodes we get, plus making use of a new non-blocking wait syscall I added to Helios:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;DG_GetKey&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pressed&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;sysret&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;sys_nbwait&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;irq1_notify&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
		&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}

	&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;scancode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;ioport_in8&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;ps2&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0x60&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;irq_ack&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;irq1&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mask&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;number&quot;&gt;7&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pressed&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;scancode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mask&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;scancode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;scancode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; ~&lt;span class=&quot;constant variable&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;scancode&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;K_AD05&lt;/span&gt;:
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_ENTER&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;K_AE08&lt;/span&gt;:
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_UPARROW&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;K_AD07&lt;/span&gt;:
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_LEFTARROW&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;K_AD08&lt;/span&gt;:
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_DOWNARROW&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;K_AD09&lt;/span&gt;:
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_RIGHTARROW&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;K_AB03&lt;/span&gt;:
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_FIRE&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;K_AB06&lt;/span&gt;:
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_USE&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;:
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_ESCAPE&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}

	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;doomKey&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, uh, we have a problem. Here’s what I ended up doing for DG_SleepMs:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ticks&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;DG_SleepMs&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ms&lt;/span&gt;)
{
	&lt;span class=&quot;comment&quot;&gt;// TODO: sleep properly&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;int64_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_ms&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ms&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;_ms&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
		&lt;span class=&quot;constant variable function&quot;&gt;sys_yield&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;ticks&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;_ms&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
}

&lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;DG_GetTicksMs&lt;/span&gt;()
{
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ticks&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some fellow on IRC said he’d implement a sleep syscall for Helios, but didn’t have time before I was ready to carry on with this port. So instead of trampling on his feet, I just yielded the thread (which immediately returns to the caller, since there are no other threads at this point) and pretend it took 5ms to do so, hoping for the best. It does not work! This port plays at wildly different speeds depending on the performance of the hardware you run it on.&lt;/p&gt;&lt;p&gt;I’m not too torn up about it, though. My goal was not to make a particularly nice or fully featured port of Doom. The speed is problematic, I hardcoded the shareware doom1.wad as the only supported level, you can’t save the game, and it crashes when you try to pick up the shotgun. But it does its job: it demonstrates the maturity of the kernel’s features thus far and provides good feedback on the API design and real-world utility.&lt;/p&gt;&lt;p&gt;If you’d like to try it, you can &lt;a href=&quot;https://redacted.moe/f/0f2b716a.iso&quot; target=&quot;_blank&quot;&gt;download a bootable ISO&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;You can run it on qemu like so:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ qemu-system-x86_64 -m 1G -no-reboot -no-shutdown \
		-drive file=doom.iso,format=raw \
		-display sdl \
		-chardev stdio,id=char0 \
		-serial chardev:char0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Enter to start, WASD to move, right shift to fire, space to open doors. It &lt;em&gt;might&lt;/em&gt; work on real hardware, but the framebuffer stuff is pretty hacky and not guaranteed to work on most stuff, and the PS/2 keyboard driver will only work with a USB keyboard if you have legacy USB emulation configured in your BIOS, and even then it might not work well. YMMV. It works on my ThinkPad X230. Have fun!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Porting-DOOM-to-Helios/</link>
        
        <pubDate>Fri, 01 Jul 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Porting-DOOM-to-Helios/</guid>
      </item>
    
      <item>
        
        
          <title>GitHub Copilot and open source laundering</title>
          <description>
            &lt;p&gt;&lt;em&gt;Disclaimer: I am the founder of a company which competes with GitHub. I am also a long-time advocate for and developer of free and open source software, with a broad understanding of free and open source software licensing and philosophy. I will not name my company in this post to reduce the scope of my conflict of interest.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We have seen an explosion in machine learning in the past decade, alongside an explosion in the popularity of free software. At the same time as FOSS has come to dominate software and found its place in almost all new software products, machine learning has increased dramatically in sophistication, facilitating more natural interactions between humans and computers. However, despite their parallel rise in computing, these two domains remain philosophically distant.&lt;/p&gt;&lt;p&gt;Though some audaciously-named companies might suggest otherwise, the machine learning space has enjoyed almost none of the freedoms forwarded by the free and open source software movement. Much of the actual code related to machine learning is publicly available, and there are many public access research papers available for anyone to read. However, the key to machine learning is access to a high-quality dataset and heaps of computing power to process that data, and these two resources are still kept under lock and key by almost all participants in the space.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The essential barrier to entry for machine learning projects is overcoming these two problems, which are often very costly to secure. A high-quality, well tagged data set generally requires thousands of hours of labor to produce,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; a task which can potentially cost millions of dollars. Any approach which lowers this figure is thus very desirable, even if the cost is making ethical compromises. With Amazon, it takes the form of gig economy exploitation. With GitHub, it takes the form of disregarding the terms of free software licenses. In the process, they built a tool which facilitates the large-scale laundering of free software into non-free software by their customers, who GitHub offers plausible deniability through an inscrutable algorithm.&lt;/p&gt;&lt;p&gt;Free software is not an unqualified gift. There are terms for its use and re-use. Even so-called “liberal” software licenses impose requirements on re-use, such as attribution. To quote the MIT license:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Permission is hereby granted […] subject to the following conditions:&lt;/p&gt;&lt;p&gt;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Or the equally “liberal” BSD license:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:&lt;/p&gt;&lt;p&gt;Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;On the other end of the spectrum, copyleft licenses such as GNU General Public License or Mozilla Public License go further, demanding not only attribution for derivative works, but that such derived works are &lt;em&gt;also released&lt;/em&gt; with the same license. Quoting GPL:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:&lt;/p&gt;&lt;p&gt;[…]&lt;/p&gt;&lt;p&gt;You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;And MPL:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Free software licenses impose obligations on the user through terms governing attribution, sublicensing, distribution, patents, trademarks, and relationships with laws like the Digital Millennium Copyright Act. The free software community is no stranger to the difficulties in enforcing compliance with these obligations, which some groups view as too onerous. But as onerous as one may view these obligations to be, one is nevertheless required to comply with them. If you believe that the force of copyright should protect your proprietary software, then you must agree that it equally protects open source works, despite the inconvenience or cost associated with this truth.&lt;/p&gt;&lt;p&gt;GitHub’s Copilot is trained on software governed by these terms, and it fails to uphold them, and enables customers to accidentally fail to uphold these terms themselves. Some argue about the risks of a “copyleft surprise”, wherein someone incorporates a GPL licensed work into their product and is surprised to find that they are obligated to release their product under the terms of the GPL as well. Copilot institutionalizes this risk and any user who wishes to use it to develop non-free software would be well-advised not to do so, else they may find themselves legally liable to uphold these terms, perhaps ultimately being required to release their works under the terms of a license which is undesirable for their goals.&lt;/p&gt;&lt;p&gt;Essentially, the argument comes down to whether or not the model constitutes a derivative work of its inputs. Microsoft argues that it does not. However, these licenses are not specific regarding the means of derivation; the classic approach of copying and pasting from one project to another need not be the only means for these terms to apply. The model exists as the result of applying an algorithm to these inputs, and thus the model itself is a derivative work of its inputs. The model, then used to create new programs, forwards its obligations to those works.&lt;/p&gt;&lt;p&gt;All of this assumes the best interpretation of Microsoft’s argument, with a heavy reliance on the fact that the model becomes a general purpose programmer, having meaningfully learned from its inputs and applying this knowledge to produce original work. Should a human programmer take the same approach, studying free software and applying those lessons, but not the code itself, to original projects, I would agree that their applied knowledge is not creating derivative works. However, that is not how machine learning works. Machine learning is essentially a glorified pattern recognition and reproduction engine, and does not represent a genuine generalization of the learning process. It is perhaps capable of a limited amount of originality, but is also capable of degrading to the simple case of copy and paste. Here is an example of Copilot reproducing, verbatim, a function which is governed by the GPL, and would thus be governed by its terms:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;video autoplay muted controls&gt;
&lt;source src=&quot;https://redacted.moe/f/dd1d5a73.mp4&quot;&gt;
&lt;/video&gt;
&lt;figcaption&gt;Source: Armin Ronacher via Twitter&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;The license reproduced by Copilot is not correct, neither in form nor function. This code was not written by V. Petkov and the GPL imposes much stronger obligations than those suggested by the comment. This small example was deliberately provoked with a suggestive prompt (this famous function is known as the “&lt;a href=&quot;https://en.wikipedia.org/wiki/Fast_inverse_square_root&quot; target=&quot;_blank&quot;&gt;fast inverse square root&lt;/a&gt;”) and the “float Q_”, but it’s not a stretch to assume someone can accidentally do something similar with any particularly unlucky English-language description of their goal.&lt;/p&gt;&lt;p&gt;Of course, the use of a suggestive prompt to convince Copilot to print GPL licensed code suggests another use: deliberately laundering FOSS source code. If Microsoft’s argument holds, then indeed the only thing which is necessary to legally circumvent a free software license is to teach a machine learning algorithm to regurgitate a function you want to use.&lt;/p&gt;&lt;p&gt;This is a problem. I have two suggestions to offer to two audiences: one for GitHub, and another for free software developers who are worried about Copilot.&lt;/p&gt;&lt;p&gt;To GitHub: this is your Oracle v Google moment. You’ve invested in building a platform on top of which the open source revolution was built, and leveraging this platform for this move is a deep betrayal of the community’s trust. The law applies to you, and banking on the fact that the decentralized open source community will not be able to mount an effective legal challenge to your $7.5B Microsoft war chest does not change this. The open source community is astonished, and the astonishment is slowly but surely boiling over into rage as our concerns fall on deaf ears and you push forward with the Copilot release. I expect that if the situation does not change, you will find a group motivated enough to challenge this. The legitimacy of the free software ecosystem may rest on this problem, and there are many companies who are financially incentivized to see to it that this legitimacy stands. I am certainly prepared to join a class action lawsuit as a maintainer, or alongside other companies with interests in free software making use of our financial resources to facilitate a lawsuit.&lt;/p&gt;&lt;p&gt;The tool can be improved, probably still in time to avoid the most harmful effects (harmful to your business, that is) of Copilot. I offer the following specific suggestions:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Allow GitHub users and repositories to opt-out of being incorporated into the model. Better, allow them to opt-in. Do not tie this flag into unrelated projects like Software Heritage and the Internet Archive.&lt;/li&gt;&lt;li&gt;Track the software licenses which are incorporated into the model and inform users of their obligations with respect to those licenses.&lt;/li&gt;&lt;li&gt;Remove copyleft code from the model entirely, unless you want to make the model and its support code free software as well.&lt;/li&gt;&lt;li&gt;Consider compensating the copyright owners of free software projects incorporated into the model with a margin from the Copilot usage fees, in exchange for a license permitting this use.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Your current model probably needs to be thrown out. The GPL code incorporated into it entitles anyone who uses it to receive a GPL’d copy of the model for their own use. It entitles these people to commercial use, to build a competing product with it. But, it presumably also includes works under incompatible licenses, such as the CDDL, which is… problematic. The whole thing is a legal mess.&lt;/p&gt;&lt;p&gt;I cannot speak for the rest of the community that have been hurt by this project, but for my part, I would be okay with not pursuing the answers to any of these questions with you in court if you agreed to resolve these problems now.&lt;/p&gt;&lt;p&gt;And, my advice to free software maintainers who are pissed that their licenses are being ignored. First, don’t use GitHub and your code will not make it into the model (for now). &lt;a href=&quot;https://drewdevault.com/2022/03/29/free-software-free-infrastructure.html&quot; target=&quot;_blank&quot;&gt;I’ve written before&lt;/a&gt; about why it’s generally important for free software projects to use free software infrastructure, and this only re-enforces that fact. Furthermore, the old “vote with your wallet” approach is a good way to show your disfavor. That said, if it occurs to you that you &lt;em&gt;don’t&lt;/em&gt; actually pay for GitHub, then you may want to take a moment to consider if the incentives created by that relationship explain this development and may lead to more unfavorable outcomes for you in the future.&lt;/p&gt;&lt;p&gt;You may also be tempted to solve this problem by changing your software licenses to prohibit this behavior. I’ll say upfront that according to Microsoft’s interpretation of the situation (invoking fair use), it doesn’t matter to them which license you use: they’ll use your code regardless. In fact, &lt;a href=&quot;https://twitter.com/ChrisGr93091552/status/1539731632931803137&quot; target=&quot;_blank&quot;&gt;some proprietary code&lt;/a&gt; was found to have been incorporated into the model. However, I still support your efforts to address this in your software licenses, as it provides an even stronger legal foundation upon which we can reject Copilot.&lt;/p&gt;&lt;p&gt;I will caution you that the way you approach that clause of your license is important. Whenever writing or changing a free and open source software license, you should consider whether or not it will still qualify as free or open source after your changes. To be specific, a clause which outright forbids the use of your code for training a machine learning model will make your software &lt;em&gt;non-free&lt;/em&gt;, and I do not recommend this approach. Instead, I would update your licenses to clarify that incorporating the code into a machine learning model is considered a form of derived work, and that your license terms apply to the model and any works produced with that model.&lt;/p&gt;&lt;p&gt;To summarize, I think that GitHub Copilot is a bad idea as designed. It represents a flagrant disregard of FOSS licensing in of itself, and it enables similar disregard — deliberate or otherwise — among its users. I hope they will heed my suggestions, and I hope that my words to the free software community offer some concrete ways to move forward with this problem.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Copilot-GPL-washing/</link>
        
        <pubDate>Thu, 23 Jun 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Copilot-GPL-washing/</guid>
      </item>
    
      <item>
        
        
          <title>Introducing the Himitsu keyring &amp; password manager for Unix</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://himitsustore.org&quot; target=&quot;_blank&quot;&gt;Himitsu&lt;/a&gt; is a new approach to storing secret information on Unix systems, such as passwords or private keys, and I released version 0.1 this morning. It’s available on &lt;a href=&quot;https://wiki.alpinelinux.org/wiki/Himitsu&quot; target=&quot;_blank&quot;&gt;Alpine Linux&lt;/a&gt; community and the &lt;a href=&quot;https://wiki.archlinux.org/title/Himitsu&quot; target=&quot;_blank&quot;&gt;Arch User Repository&lt;/a&gt;, with &lt;a href=&quot;https://repology.org/project/himitsu/versions&quot; target=&quot;_blank&quot;&gt;more distributions&lt;/a&gt; hopefully on the way soon.&lt;/p&gt;&lt;p&gt;So, what is Himitsu and what makes it special? The following video introduces the essential concepts and gives you an idea of what’s possible:&lt;/p&gt;&lt;p&gt;&lt;video src=&quot;https://himitsustore.org/intro.mp4&quot; controls&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;If you prefer reading to watching, this blog post includes everything that’s in the video.&lt;/p&gt;&lt;h2&gt;What is Himitsu?&lt;/h2&gt;&lt;p&gt;Himitsu draws inspiration from Plan 9’s &lt;a href=&quot;http://man.9front.org/4/factotum&quot; target=&quot;_blank&quot;&gt;factotum&lt;/a&gt;, but polished up and redesigned for Unix. At its core, Himitsu is a key/value store and a simple protocol for interacting with it. For example, a web login could be stored like so:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;proto=web host=example.org user=jdoe password!=hunter2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Himitsu has no built-in knowledge of web logins, it just stores arbitrary keys and values. The bang (!) indicates that the password is a “secret” value, and the “proto” key defines additional conventions for each kind of secret. For proto=web, each key/value pair represents a form field on a HTML login form.&lt;/p&gt;&lt;p&gt;We can query the key store using the “hiq” command. For instance, we can obtain the example key above by querying for any key with “proto=web”, any “host”, “user”, and “password” value, and an optional “comment” value:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ hiq proto=web host user password! comment?
proto=web host=example.org user=jdoe password!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You’ll notice that the password is hidden here. In order to obtain it, we must ask for the user’s consent.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ hiq -d proto=web host user password! comment?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/85eb1b52.png&quot;&gt;
&lt;figcaption&gt;A screenshot of a GTK+ dialog confirming the operation&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;proto=web host=example.org user=jdoe password!=hunter2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also use hiq to add or delete keys, or incorporate it into a shell pipeline:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ hiq -dFpassword host=example.org
hunter2
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;A simple, extensible protocol&lt;/h2&gt;&lt;p&gt;The protocol is a simple line-oriented text protocol, which is documented in the &lt;a href=&quot;https://himitsustore.org/docs/himitsu-ipc.5.html&quot; target=&quot;_blank&quot;&gt;himitsu-ipc(5)&lt;/a&gt; manual page. We can also use it via netcat:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ nc -U $XDG_RUNTIME_DIR/himitsu
query host=example.org
key proto=web host=example.org user=jdoe password!
end
query -d host=example.org
key proto=web host=example.org user=jdoe password!=hunter2
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The consent prompter also uses a standardized protocol, documented by &lt;a href=&quot;https://himitsustore.org/docs/himitsu-prompter.5.html&quot; target=&quot;_blank&quot;&gt;himitsu-prompter(5)&lt;/a&gt;. Based on this, you can implement new prompters for Qt, or the TTY, or any other technology appropriate to your system, or implement a more novel approach, such as sending a push notification to your phone to facilitate consent.&lt;/p&gt;&lt;h2&gt;Additional frontends&lt;/h2&gt;&lt;p&gt;Based on these protocols, a number of additional integrations are possible. Martijn Braam has written a nice GTK+ frontend called &lt;a href=&quot;https://git.sr.ht/~martijnbraam/keyring/&quot; target=&quot;_blank&quot;&gt;keyring&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://brixitcdn.net/metainfo/keyring.png&quot;&gt;
&lt;figcaption&gt;A screenshot of the GTK+ frontend&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;There’s also a &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/himitsu-integration/&quot; target=&quot;_blank&quot;&gt;Firefox add-on&lt;/a&gt; which auto-fills forms for keys with proto=web:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/73328356.png&quot;&gt;
&lt;figcaption&gt;Screenshot of himitsu-firefox&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;We also have a package called &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu-ssh&quot; target=&quot;_blank&quot;&gt;himitsu-ssh&lt;/a&gt; which provides an SSH agent:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ hissh-import &amp;lt; ~/.ssh/id_ed25519
Enter SSH key passphrase: 
key proto=ssh type=ssh-ed25519 pkey=pF7SljE25sVLdWvInO4gfqpJbbjxI6j+tIUcNWzVTHU= skey! comment=sircmpwn@homura
$ ssh-add -l
256 SHA256:kPr5ZKTNE54TRHGSaanhcQYiJ56zSgcpKeLZw4/myEI sircmpwn@homura (ED25519)
$ ssh git@git.sr.ht
Hi sircmpwn! You&amp;apos;ve successfully authenticated, but I do not provide an interactive shell. Bye!
Connection to git.sr.ht closed.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I hope to see an ecosystem of tools built around Himitsu to grow. New frontends like keyring would be great, and new integrations like GPG agents would also be nice to see.&lt;/p&gt;&lt;h2&gt;Zero configuration&lt;/h2&gt;&lt;p&gt;Himitsu-aware software can discover your credentials and connection details without any additional configuration. For example, a mail client might look for &lt;code&gt;proto=imap&lt;/code&gt; and &lt;code&gt;proto=smtp&lt;/code&gt; and discover something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;proto=imap host=imap.migadu.com user=drew@ddevault.org password! port=993 enc=tls
proto=smtp host=imap.migadu.com user=drew@ddevault.org password! port=465 enc=tls
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After a quick consent prompt, the software can load your IMAP and SMTP configuration and get connected without any manual steps. With an agent like himitsu-ssh, it could even connect without actually handling your credentials directly — a use-case we want to support with improvements to the prompter UI (to distinguish between a case where an application will &lt;em&gt;view&lt;/em&gt; versus &lt;em&gt;use&lt;/em&gt; your credentials).&lt;/p&gt;&lt;h2&gt;The cryptography&lt;/h2&gt;&lt;p&gt;Your key store is located at $XDG_DATA_HOME/himitsu/. The key is derived by mixing your password with argon2, and the resulting key is used for AEAD with XChaCha20+Poly1305. The “index” file contains a list of base64-encoded encrypted blobs, one per line, enumerating the keys in the key store.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Secret keys are encrypted and stored separately in files in this directory. If you like the pass approach to storing your keys in git, you can easily commit this directory to a git repository, or haul it along to each of your devices with whatever other means is convenient to you.&lt;/p&gt;&lt;p&gt;Himitsu is written in Hare and uses cryptography primitives available from its standard library. Note that these have not been audited.&lt;/p&gt;&lt;h2&gt;Future plans&lt;/h2&gt;&lt;p&gt;I’d like to expand on Himitsu in the future. One idea is to store your full disk encryption password in Himitsu and stick a subset of your key store into the initramfs, which you unlock during early boot, pull FDE keys out of, and then pre-authorize the keyring for your desktop session - which you’re logged in to automatically on the basis that you were pre-authorized during boot.&lt;/p&gt;&lt;p&gt;We also want to add key sharing and synchronization tools. The protocol could easily be moved to TCP and authorized with your existing key store key (we could make an ed25519 key out of it, or generate and store one separately), so setting up key synchronization might be as simple as:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ hiq -a proto=sync host=himitsu.sr.ht
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You could also use Himitsu for service discovery — imagine a key ring running on your datacenter LAN with entries for your Postgres database, SMTP credentials, and so on.&lt;/p&gt;&lt;p&gt;There are some other ideas that we could use your help with:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;himitsu-firefox improvements (web devs welcome!)&lt;/li&gt;&lt;li&gt;Chromium support (web devs welcome!)&lt;/li&gt;&lt;li&gt;Himitsu apps for phones (mobile devs welcome!)&lt;/li&gt;&lt;li&gt;More key management frontends (maybe a TUI?)&lt;/li&gt;&lt;li&gt;More security options — smart cards? U2F?&lt;/li&gt;&lt;li&gt;hare-ssh improvements (e.g. RSA keys)&lt;/li&gt;&lt;li&gt;PGP support&lt;/li&gt;&lt;li&gt;Anything else you can think of&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Please join us! We hang out on IRC in #himitsu on Libera Chat. Give Himitsu a shot and let us know what you think.&lt;/p&gt;&lt;p&gt;Alright, back to kernel hacking. I got multi-tasking working yesterday!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Himitsu/</link>
        
        <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Himitsu/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, June 2022</title>
          <description>
            &lt;p&gt;Hello again! I would like to open this post by acknowledging the response to my earlier post, “bleh”. Since it was published, I have received several hundred emails expressing support and kindness. I initially tried to provide these with thoughtful replies, then shorter replies, then I had to stop replying at all, but I did read every one. Thank you, everyone, for sending these. I appreciate it very much, and it means a lot to me.&lt;/p&gt;&lt;p&gt;I have actually had a lot more fun programming this month than usual, since I decided to spend more time on experimental and interesting projects and less time on routine maintenance or long-term developments. So, the feature you’ve been waiting for in SourceHut might be delayed, but in return, there’s cool progress on the projects that you didn’t even know you were waiting for. Of course, the SourceHut workload never dips below a dull roar, as I have to attend to business matters and customer support promptly, and keep a handle on the patch queue, and the other SourceHut staff and contributors are always hard at work — so there’ll be plenty to discuss in the “what’s cooking” later.&lt;/p&gt;&lt;p&gt;The bulk of my focus has been on the Helios kernel this month, a project &lt;a href=&quot;https://drewdevault.com/2022/06/13/helios.html&quot; target=&quot;_blank&quot;&gt;I introduced&lt;/a&gt; a couple of days ago. I spent a lot of time furiously refactoring, reworking the existing kernel code for evalutaing features like page allocation and virtual address space management into capability-oriented kernel services that can be provided to userspace, then overhauling our startup code to provision a useful set of capabilities for the init process to take advantage of. I also implemented x86_64 I/O port services, which allowed for the first few drivers to be written in userspace — serial ports and simple VBE graphics. We also got interrupts working properly and brought up the PIT, which is another major step towards multi-tasking. I also implemented a new syscall ABI with error handling, and refactored a lot of the arch-specific code to make new ports easier. The kernel is in a much better state now than it was a month ago (and to think it’s only three months old!).&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/7e43ce39.jpg&quot;&gt;
&lt;figcaption&gt;A picture of Helios drawing to a framebuffer&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;There was also a lot of progress on &lt;a href=&quot;https://sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;Himitsu&lt;/a&gt;, which I plan on presenting in a video and blog post in a few days time. The Firefox add-on actually works now (though some features remain to be done), and Alexey Yerin fixed several important bugs and contributed several new features. The user is now prompted to consent before deleting keys, and we have a new GTK+ prompter written in Python, which is much more reliable and feature-full thanks to Martijn Braam’s help (rewriting it in C again is a long-term TODO item for any interested contributor). I also made some progress towards what will ultimately become full-disk encryption support.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://brixitcdn.net/metainfo/keyring.png&quot;&gt;
&lt;figcaption&gt;himitsu-keyring, a new GTK+ keyring manager from Martijn Braam&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Hare also enjoyed many improvements this month. We have some new improvements to date/time support, including fixes for Martian time ;) I also mostly implemented cross-compiling, which you can try out with &lt;code&gt;hare build -t riscv64&lt;/code&gt; or something similar. The major outstanding pain point here is that the Hare cache is not arch-aware, so you need to &lt;code&gt;rm -rf ~/.cache/hare&lt;/code&gt; each time you switch architectures for now. We now have complex number support, as well as improvements to encoding::json and net::uri.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/737ed5b7.png&quot;&gt;
&lt;figcaption&gt;A screenshot of a fractal rendered with the aid of Hare’s new complex number support&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;That’s all for today. Until next time!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-June-2022/</link>
        
        <pubDate>Wed, 15 Jun 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-June-2022/</guid>
      </item>
    
      <item>
        
        
          <title>The Helios microkernel</title>
          <description>
            &lt;p&gt;I’ve been working on a cool project lately that I’d like to introduce you to: &lt;a href=&quot;https://sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;the Helios microkernel&lt;/a&gt;. Helios is written in &lt;a href=&quot;https://harelang.org&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt; and currently targets x86_64, and riscv64 and aarch64 are on the way. It’s very much a work-in-progress: don’t expect to pick this up and start building anything with it today.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/4d2d7b25.jpg&quot;&gt;
&lt;figcaption&gt;A picture of a ThinkPad running Helios, demonstrating userspace memory allocation&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Drawing some inspiration from seL4, Helios uses a capability-based design for isolation and security. The kernel offers primitives for allocating physical pages, mapping them into address spaces, and managing tasks, plus features like platform-specific I/O (e.g. reading and writing x86 ports). The entire system is written in Hare, plus some necessary assembly for the platform bits (e.g. configuring the GDT or IDT).&lt;/p&gt;&lt;p&gt;Things are still quite early, but I’m pretty excited about this project. I haven’t had this much fun hacking in some time :) We have several kernel services working, including memory management and virtual address spaces, and I’ve written a couple of simple drivers in userspace (serial and BIOS VGA consoles). Next up is preemptive multi-tasking — we already have interrupts working reliably, including the PIT, so all that’s left for multi-tasking is to actually implement the context switch. I’d like to aim for an seL4-style single-stack system, though some finageling will be required to make that work.&lt;/p&gt;&lt;p&gt;Again, much of the design comes from seL4, but unlike seL4, we intend to build upon this kernel and develop a userspace as well. Each of the planned components is named after celestial bodies, getting further from the sun as they get higher-level:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Helios: the kernel&lt;/li&gt;&lt;li&gt;Mercury: low-level userspace services &amp; service bus&lt;/li&gt;&lt;li&gt;Venus: real-world driver collection&lt;/li&gt;&lt;li&gt;Gaia: high-level programming environment&lt;/li&gt;&lt;li&gt;Ares: a complete operating system; package management, GUI, etc&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A few other components are planned — “Vulcan” is the userspace kernel testing framework, named for the (now disproved) hypothetical planet between Mercury and the Sun, and “Luna” is the planned POSIX compatibility layer. One of the goals is to be practical for use on real-world hardware. I’ve been testing it continuously on my ThinkPads to ensure real-world hardware support, and I plan on writing drivers for its devices — Intel HD graphics, HD Audio, and Intel Gigabit Ethernet at the least. A basic AMD graphics driver is also likely to appear, and perhaps drivers for some SoC’s, like Raspberry Pi’s VideoCore. I have some neat ideas for the higher-level components as well, but I’ll save those for later.&lt;/p&gt;&lt;p&gt;Why build a new operating system? Well, for a start, it’s really fun. But I also take most of my projects pretty seriously and aim for real-world usability, though it remains to be seen if this will be achieved. This is a hugely ambitious project, or, in other words, my favorite kind of project. Even if it’s not ultimately useful, it will drive the development of a lot of useful stuff. We’re planning to design a debugger that will be ported to Linux as well, and we’ll be developing DWARF support for Hare to facilitate this. The GUI toolkit we want to build for Ares will also be generally applicable. And Helios and Mercury together have a reasonably small scope and makes for an interesting and useful platform in their own right, even if the rest of the stack never completely materializes. If nothing else, it will probably be able to run DOOM fairly soon.&lt;/p&gt;&lt;p&gt;The kernel &lt;em&gt;is&lt;/em&gt; a microkernel, so it is fairly narrow in scope and will probably be more-or-less complete in the foreseeable future. The next to-do items are context switching, so we can set up multi-tasking, IPC, fault handling, and userspace support for interrupts. We’ll also need to parse the ACPI tables and bring up PCI in the kernel before handing it off to userspace. Once these things are in place, the kernel is essentially ready to be used to write most drivers, and the focus will move to fleshing out Mercury and Venus, followed by a small version of Gaia that can at least support an interactive shell. There are some longer-term features which will be nice to have in the kernel at some point, though, such as SMP, IOMMU, or VT-x support.&lt;/p&gt;&lt;p&gt;Feel free to pull down the code and check it out, though remember my warning that it doesn’t do too much yet. You can download the &lt;a href=&quot;https://builds.sr.ht/~sircmpwn/helios/commits/master&quot; target=&quot;_blank&quot;&gt;latest ISO&lt;/a&gt; from the CI, if you want to reproduce the picture at the top of this post, and write it to a flash drive to stick in the x86_64 computer of your choice (boot via legacy BIOS). If you want to mess with the code, you could play around with the Vulcan system to get simple programs running in userspace. The kernel serial driver is write-only, but a serial driver written in userspace could easily be made to support interactive programs. If you’re feeling extra adventureous, it probably wouldn’t be too difficult to get a framebuffer online and draw some pixels — ping me in #helios on Libera Chat for a few words of guidance if you want to try it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/helios/</link>
        
        <pubDate>Mon, 13 Jun 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/helios/</guid>
      </item>
    
      <item>
        
        
          <title>bleh</title>
          <description>
            &lt;p&gt;A few weeks ago, the maintainer of a project on SourceHut stepped down from their work, citing harassment over using SourceHut as their platform of choice. It was a difficult day when I heard about that.&lt;/p&gt;&lt;p&gt;Over the past few weeks, I have been enduring a bit of a depressive episode. It’s a complex issue rooted in several different problems, but I think a major source of it is the seemingly constant deluge of hate I find myself at the receiving end of online. I had to grow a thick skin a long time ago, but lately it has not been thick enough. I am finding it increasingly difficult to keep up with my work.&lt;/p&gt;&lt;p&gt;Perhaps it this has something to do with the backlash, not just against me and my work, but against others who use and participate in that work. It’s not enough to dislike my programming language, but the skeptics must publicly denounce it and discourage others from using it. It’s irresponsible, if not immoral, to design a language without a borrow checker in 2022. SourceHut’s email-oriented approach might not be for everyone, and instead of simply not using it, skeptics must harass any projects that do. This kind of harassment is something I hear about often from many maintainers of projects on SourceHut. It breaks my heart and I feel helpless to do anything about it.&lt;/p&gt;&lt;p&gt;I’m also often dealing with harassment directed at me alone. When I complained this week about being DDoSed by a company with over a billion dollars in annual revenue, it was portrayed as righteous retribution and a sign of incompetence. I can’t even count the number of times someone has said they would refuse to use SourceHut (and that you, too, dear reader, should avoid it) on the sole basis that I’m involved with it. There is a steady supply of vile comments about me based on “facts” delivered from the end of a game of telephone in which every participant hates my guts, all easily believable without further research because I’m such a villainous character. Every project I work on, every blog post I write, even many of the benign emails to public lists or GitHub issues I open — the response is just vitriol.&lt;/p&gt;&lt;p&gt;I have made no shortage of mistakes, and there are plenty of hurt feelings which can be laid at my feet. I am regretful for my mistakes, and I have worked actively to improve. I think that it has been working. Perhaps that’s arrogant of me to presume, but I’m not sure what else to do. Must I resign myself to my fate for stupid comments I made years ago? I’m sorry, and I’ve been working to do better. Can I have another chance?&lt;/p&gt;&lt;p&gt;For some I think the answer is “no”. Many of my detractors just want me to shut up. No more blog posts, no new projects. Just go away, Drew.&lt;/p&gt;&lt;p&gt;Well, I can’t say it’s not working. This stuff gets to me. At times like this I have very little motivation to work. If you’re looking for a strategy to get me to shut up, just ensure that I have a constant flow of toxic comments to read.&lt;/p&gt;&lt;p&gt;I love writing code, at least most of the time. I believe in my principles and I enjoy writing software that embodies them. I love doing it, and I’m really good at it, and thousands of people are depending on my work.&lt;/p&gt;&lt;p&gt;I’m doing the work that I believe in, and working with people who share those values. I have worked very hard for that privilege. I’m sorry that it’s not good enough for many people. I’m just trying to do my best. And if you must harass anyone over it, at least harass me, and not anyone else. My inbox is at &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;drew@ddevault.org&lt;/a&gt;, and I promise that I will read your email and cry, so that no one else has to.&lt;/p&gt;&lt;p&gt;I’ll close by thanking those who have sent me positive notes. Some of these comments are very touching. If you’ve sent one of these, you have my thanks. Love you :)&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/bleh/</link>
        
        <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/bleh/</guid>
      </item>
    
      <item>
        
        
          <title>Google has been DDoSing SourceHut for over a year</title>
          <description>
            &lt;p&gt;Just now, I took a look at the HTTP logs on git.sr.ht. Of the past 100,000 HTTP requests received by git.sr.ht (representing about 2½ hours of logs), 4,774 have been requested by GoModuleProxy — 5% of all traffic. And their requests are not cheap: every one is a complete git clone. They come in bursts, so every few minutes we get a big spike from Go, along with a constant murmur of Go traffic.&lt;/p&gt;&lt;p&gt;This has been ongoing since around the release of Go 1.16, which came with some changes to how Go uses modules. Since this release, following a gradual ramp-up in traffic as the release was rolled out to users, git.sr.ht has had a constant floor of I/O and network load for which the majority can be attributed to Go.&lt;/p&gt;&lt;p&gt;I started to suspect that something strange was going on when our I/O alarms started going off in February 2021 (we eventually had to tune these alarms up above the floor of I/O noise generated by Go), correlated with lots of activity from a Go user agent. I was able to narrow it down with some effort, but to the credit of the Go team they did &lt;a href=&quot;https://github.com/golang/go/issues/44468&quot; target=&quot;_blank&quot;&gt;change their User-Agent to make more apparent what was going on&lt;/a&gt;. Ultimately, this proved to be the end of the Go team’s helpfulness in this matter.&lt;/p&gt;&lt;p&gt;I did narrow it down: it turns out that the Go Module Mirror runs some crawlers that periodically clone Git repositories with Go modules in them to check for updates. Once we had narrowed this down, I filed &lt;a href=&quot;https://github.com/golang/go/issues/44577&quot; target=&quot;_blank&quot;&gt;a second ticket&lt;/a&gt; to address the problem.&lt;/p&gt;&lt;p&gt;I came to understand that the design of this feature is questionable. For a start, I never really appreciated the fact that Go secretly calls home to Google to fetch modules through a proxy (you can set &lt;a href=&quot;https://drewdevault.com/2021/08/06/goproxy-breaks-go.html&quot; target=&quot;_blank&quot;&gt;GOPROXY=direct&lt;/a&gt; to fix this). Even taking the utility at face value, however, the implementation leaves much to be desired. The service is distributed across many nodes which all crawl modules independently of one another, resulting in very redundant git traffic.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;140 8a42ab2a4b4563222b9d12a1711696af7e06e4c1092a78e6d9f59be7cb1af275
 57 9cc95b73f370133177820982b8b4e635fd208569a60ec07bd4bd798d4252eae7
 44 9e730484bdf97915494b441fdd00648f4198be61976b0569338a4e6261cddd0a
 44 80228634b72777eeeb3bc478c98a26044ec96375c872c47640569b4c8920c62c
 44 5556d6b76c00cfc43882fceac52537b2fdaa7dff314edda7b4434a59e6843422
 40 59a244b3afd28ee18d4ca7c4dd0a8bba4d22d9b2ae7712e02b1ba63785cc16b1
 40 51f50605aee58c0b7568b3b7b3f936917712787f7ea899cc6fda8b36177a40c7
 40 4f454b1baebe27f858e613f3a91dfafcdf73f68e7c9eba0919e51fe7eac5f31b
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a sample from &lt;a href=&quot;https://paste.sr.ht/~sircmpwn/b46ad0b13e864923df80cb8e8285bf1661e6f872&quot; target=&quot;_blank&quot;&gt;a larger set&lt;/a&gt; which shows the hashes of git repositories on the right (names were hashed for privacy reasons), and the number of times they were cloned over the course of an hour. The main culprit is the fact that the nodes all crawl independently and don’t communicate with each other, but the per-node stats are not great either: each IP address still clones the same repositories 8-10 times per hour. &lt;a href=&quot;https://github.com/golang/go/issues/44577#issuecomment-851079949&quot; target=&quot;_blank&quot;&gt;Another user&lt;/a&gt; hosting their own git repos noted a single module being downloaded over 500 times in a single day, generating 4 GiB of traffic.&lt;/p&gt;&lt;p&gt;The Go team holds that this service is not a crawler, and thus they do not obey robots.txt — if they did, I could use it to configure a more reasonable “Crawl-Delay” to control the pace of their crawling efforts. I also suggested keeping the repositories stored on-site and only doing a git fetch, rather than a fresh git clone every time, or using shallow clones. They could also just fetch fresh data when users request it, instead of pro-actively crawling the cache all of the time. All of these suggestions fell on deaf ears, the Go team has not prioritized it, and a year later I am still being DDoSed by Google as a matter of course.&lt;/p&gt;&lt;p&gt;I was banned from the Go issue tracker for mysterious reasons,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; so I cannot continue to nag them for a fix. I can’t blackhole their IP addresses, because that would make all Go modules hosted on git.sr.ht stop working for default Go configurations (i.e. without GOPROXY=direct). I tried to advocate for Linux distros to patch out GOPROXY by default, citing privacy reasons, but I was unsuccessful. I have no further recourse but to tolerate having our little-fish service DoS’d by a 1.38 trillion dollar company. But I will say that if I was in their position, and my service was mistakenly sending an excessive amount of traffic to someone else, I would make it my first priority to fix it. But I suppose no one will get promoted for prioritizing that at Google.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Google-has-been-DDoSing-sourcehut/</link>
        
        <pubDate>Wed, 25 May 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Google-has-been-DDoSing-sourcehut/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, May 2022</title>
          <description>
            &lt;p&gt;This was an exciting month: &lt;a href=&quot;https://harelang.org&quot; target=&quot;_blank&quot;&gt;the Hare programming language&lt;/a&gt; is a secret no more! You can now &lt;a href=&quot;https://harelang.org/tutorials/introduction/&quot; target=&quot;_blank&quot;&gt;try out&lt;/a&gt; the programming language I first teased &lt;a href=&quot;https://drewdevault.com/blog/A-new-systems-language/&quot;&gt;over a year ago&lt;/a&gt; and &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;tell me what you think&lt;/a&gt;. I hope you like it! I’m quite pleased with it so far.&lt;/p&gt;&lt;p&gt;One thing Hare has done is allow me to unshelve several projects which were blocked pending the availability of a suitable language to write them in. I have actually been working on several of these for a while now — and several more are to come later — but I couldn’t share them thanks to Hare’s policy of secrecy early in its development. Allow me to introduce you to a few projects!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/helios&quot; target=&quot;_blank&quot;&gt;Helios&lt;/a&gt;&lt;/strong&gt; is a micro-kernel for x86_64, and ideally later for aarch64 and riscv64 as well (and possibly other targets as Hare grows additional ports). We have a few things working, such as paging and interrupts, and as of this morning we have entered userspace. Next up is rigging up syscalls and scheduling, then we’re going to start fleshing out an L4-inspired API and writing some drivers in userspace.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/2c1f497a.png&quot;&gt;
&lt;figcaption&gt;A screenshot showing Helios booting and entering userspace&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;Himitsu&lt;/a&gt;&lt;/strong&gt; is a secret storage system. It can act as a password manager, but it also stores other arbitrary secret data, such as private keys. Each key is a set of key/value pairs, some of which can be secret. This allows you to store additional data alongside your password (such as your username or email for login), and also supports secret data other than passwords — like SSH keys. An extensible consent and agent protocols allow you to expand it to support a wide variety of use-cases for secure use of secrets.&lt;/p&gt;&lt;p&gt;&lt;video src=&quot;https://redacted.moe/f/09d422dd.webm&quot; muted controls&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/btqd&quot; target=&quot;_blank&quot;&gt;btqd&lt;/a&gt;&lt;/strong&gt;, or “bittorrent queue daemon”, is (going to be) a bittorrent daemon, but it is still very early in development. The design is essentially that of a process supervisor which manages a queue of torrents and fires up subprocesses to seed or leech for a set of active torrents. Each subprocess, such as btlc (bittorrent leech client), or btsc (bittorrent seed client), can also be used separately from the queue daemon. Further development is blocked on net::http, which is blocked on TLS support, for tracker announce requests. I may temporarily unblock this by shelling out to curl instead.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/scheduled&quot; target=&quot;_blank&quot;&gt;scheduled&lt;/a&gt;&lt;/strong&gt; is also early in development. It is a replacement for crond (and also &lt;a href=&quot;https://linux.die.net/man/1/at&quot; target=&quot;_blank&quot;&gt;at(1)&lt;/a&gt;) which is redesigned from the ground up. I have never been thrilled with cron’s design — it’s very un-Unix like. scheduled will have better error handling and logging, a much more flexible and understandable approach to configuration, and a better approach to security, plus the ability to do ad-hoc scheduling from the command line. This was designed prior to date/time support landing in Hare, and was blocked for a while, but is now unblocked. However, it is not my highest priority.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Each of these projects will spawn more blog posts (or talks) going into greater depth on their design goals and rationale later on. For now, with the introductions out of the way, allow me to fill you in on the things which got done in this past month in particular.&lt;/p&gt;&lt;p&gt;I’ll keep the SourceHut news short, and expand upon it in the “what’s cooking” post later today. For my own part, I spent some time working on &lt;a href=&quot;https://sr.ht/~emersion/hut&quot; target=&quot;_blank&quot;&gt;hut&lt;/a&gt; to add support for comprehensive account data import/export. This will allow you to easily take all of your data out of sourcehut and import it into another instance, or any compatible software — your git repos are just git repos and your mailing lists are just mbox files, so you could push them to GitHub or import them into GNU Mailman, for example. This work is also a step towards self-service account deletion and renaming, both prioritized for the beta.&lt;/p&gt;&lt;p&gt;Regarding Hare itself, there are many important recent developments. Over 300 commits landed this month, so I’ll have to leave some details out. An OpenBSD port is underway by Brian Callahan, and the initial patches have landed for the Hare compiler. The crypto module grew &lt;a href=&quot;https://docs.harelang.org/crypto/blowfish&quot; target=&quot;_blank&quot;&gt;blowfish&lt;/a&gt; and &lt;a href=&quot;https://docs.harelang.org/crypto/bcrypt&quot; target=&quot;_blank&quot;&gt;bcrypt&lt;/a&gt; support, both useful mainly for legacy compatibility, as well as the more immediately useful &lt;a href=&quot;https://docs.harelang.org/crypto/x25519&quot; target=&quot;_blank&quot;&gt;x25519&lt;/a&gt; and &lt;a href=&quot;https://docs.harelang.org/encoding/pem&quot; target=&quot;_blank&quot;&gt;pem&lt;/a&gt; implementations. There is also a new &lt;a href=&quot;https://docs.harelang.org/encoding/json&quot; target=&quot;_blank&quot;&gt;encoding::json&lt;/a&gt; module,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and a number of fixes and improvements have been steadily flowing in for &lt;a href=&quot;https://docs.harelang.org/regex&quot; target=&quot;_blank&quot;&gt;regex&lt;/a&gt;, &lt;a href=&quot;https://docs.harelang.org/bufio&quot; target=&quot;_blank&quot;&gt;bufio&lt;/a&gt;, &lt;a href=&quot;https://docs.harelang.org/net&quot; target=&quot;_blank&quot;&gt;net&lt;/a&gt;, &lt;a href=&quot;https://docs.harelang.org/net/uri&quot; target=&quot;_blank&quot;&gt;net::uri&lt;/a&gt;, and &lt;a href=&quot;https://docs.harelang.org/datetime&quot; target=&quot;_blank&quot;&gt;datetime&lt;/a&gt;, along with dozens of others.&lt;/p&gt;&lt;p&gt;For Himitsu, I developed &lt;a href=&quot;https://sr.ht/~sircmpwn/hare-ssh&quot; target=&quot;_blank&quot;&gt;hare-ssh&lt;/a&gt; this month to facilitate the addition of &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu-ssh&quot; target=&quot;_blank&quot;&gt;himitsu-ssh&lt;/a&gt;, which provides SSH tooling that integrates with Himitsu (check out the video above for a demo). The “hissh-import” command decodes OpenSSH private keys and loads them into the Himitsu keystore, and the “hissh-agent” command runs an SSH agent that performs authentication with the private keys stored in Himitsu. Future additions will include “hissh-export”, for getting your private keys back out in a useful format, and “hissh-keygen”, for skipping the import/export step entirely. Presently only ed25519 keys are supported; more will be added as the necessary primitives are added to Hare upstream.&lt;/p&gt;&lt;p&gt;I did some work on Helios this weekend, following a brief hiatus. I wrote a more generalized page table implementation which can manage multiple page tables (necessary to have separate address spaces for each process), and started rigging up the kernel to userspace transition, which I briefly covered earlier in the post. As of this morning, I have some code running in userspace — one variant attempts to &lt;code&gt;cli&lt;/code&gt;, causing a general protection fault (as expected), and another just runs a busy loop, which works without any faults. Next steps are syscalls and scheduling.&lt;/p&gt;&lt;p&gt;That’s all the news for today. Hare! Woo! Thanks for reading, and be sure to check out — and maybe contribute to? — some of these projects. Take care!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-May-2022/</link>
        
        <pubDate>Mon, 16 May 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-May-2022/</guid>
      </item>
    
      <item>
        
        
          <title>A Hare code generator for finding ioctl numbers</title>
          <description>
            &lt;p&gt;Modern Unix derivatives have this really bad idea called &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/functions/ioctl.html&quot; target=&quot;_blank&quot;&gt;ioctl&lt;/a&gt;. It’s a function which performs arbitrary operations on a file descriptor. It is essentially the kitchen sink of modern Unix derivatives, particularly Linux, in which they act almost like a second set of extra syscalls. For example, to get the size of the terminal window, you use an ioctl specific to TTY file descriptors:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;wsz&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;winsize&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioctl&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;TIOCGWINSZ&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;wsz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;error constant type variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;errno&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EBADFD&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;error constant variable&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ENOTTY&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;unsupported&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;Unexpected&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioctl&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; int &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ttysize&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;wsz&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ws_row&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;wsz&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ws_col&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code performs the ioctl syscall against the provided file descriptor “fd”, using the “TIOCGWINSZ” operation, and setting the parameter to a pointer to a winsize structure. There are thousands of ioctls provided by Linux, and each of them is assigned a constant like TIOCGWINSZ (0x5413). Some constants, including this one, are assigned somewhat arbitrarily. However, some are assigned with some degree of structure.&lt;/p&gt;&lt;p&gt;Consider for instance the ioctl TUNSETOWNER, which is used for tun/tap network devices. This ioctl is assigned the number 0x400454cc, but this is not selected arbitrarily. It’s assigned with a macro, which we can find in /usr/include/linux/if_tun.h:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETOWNER&lt;/span&gt;   _IOW(&amp;apos;T&amp;apos;, 204, int)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The _IOW macro, along with similar ones like _IO, _IOR, and _IOWR, are defined in /usr/include/asm-generic/ioctl.h. They combine this letter, number, and parameter type (or rather its size), and the direction (R, W, WR, or neither), OR’d together into an unsigned 32-bit number:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_IOC_WRITE&lt;/span&gt;	1U

&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;_IOC_TYPECHECK&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;) (sizeof(t))

&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;_IOC&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;dir&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;type&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;nr&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;) \
	(((dir)  &amp;lt;&amp;lt; _IOC_DIRSHIFT) | \
	 ((type) &amp;lt;&amp;lt; _IOC_TYPESHIFT) | \
	 ((nr)   &amp;lt;&amp;lt; _IOC_NRSHIFT) | \
	 ((size) &amp;lt;&amp;lt; _IOC_SIZESHIFT))

&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;_IOW&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;type&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;nr&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;)	_IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It would be useful to define ioctl numbers in a similar fashion for Hare programs. However, Hare lacks macros, so we cannot re-implement this in exactly the same manner. Instead, we can use code generation.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://harelang.org&quot; target=&quot;_blank&quot;&gt;Hare&lt;/a&gt; is a new systems programming language I’ve been working on for a couple of years. Check out the &lt;a href=&quot;https://harelang.org/blog/2022-04-25-announcing-hare/&quot; target=&quot;_blank&quot;&gt;announcement&lt;/a&gt; for more detail.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Again using the tun interface as an example, our goal is to turn the following input file:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;sock_filter&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;jt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;jf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;sock_fprog&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sock_filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETNOCSUM&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETDEBUG&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;201&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETIFF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;202&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETPERSIST&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;203&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETOWNER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;204&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETLINK&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;205&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETGROUP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;206&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETFEATURES&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;207&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETOFFLOAD&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;208&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETTXFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;209&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETIFF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;210&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETSNDBUF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;211&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETSNDBUF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;212&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNATTACHFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;213&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sock_fprog&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNDETACHFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;214&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sock_fprog&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETVNETHDRSZ&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;215&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETVNETHDRSZ&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;216&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETQUEUE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;217&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETIFINDEX&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;218&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;219&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sock_fprog&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETVNETLE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;220&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETVNETLE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;221&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETVNETBE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;222&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETVNETBE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;223&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETSTEERINGEBPF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;224&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETFILTEREBPF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;225&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETCARRIER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;226&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETDEVNETNS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IO&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;227&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Into the following output file:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;sock_filter&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;jt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;jf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;sock_fprog&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sock_filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETNOCSUM&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454c8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETDEBUG&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454c9&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETIFF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454ca&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETPERSIST&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454cb&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETOWNER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454cc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETLINK&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454cd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETGROUP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454ce&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETFEATURES&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x800454cf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETOFFLOAD&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454d0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETTXFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454d1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETIFF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x800454d2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETSNDBUF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x800454d3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETSNDBUF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454d4&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNATTACHFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x401054d5&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNDETACHFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x401054d6&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETVNETHDRSZ&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x800454d7&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETVNETHDRSZ&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454d8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETQUEUE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454d9&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETIFINDEX&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454da&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x801054db&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETVNETLE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454dc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETVNETLE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x800454dd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETVNETBE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454de&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETVNETBE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x800454df&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETSTEERINGEBPF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x800454e0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETFILTEREBPF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x800454e1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNSETCARRIER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x400454e2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETDEVNETNS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x54e3&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wrote the &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare/tree/master/item/cmd/ioctlgen/main.ha&quot; target=&quot;_blank&quot;&gt;ioctlgen&lt;/a&gt; tool for this purpose, and since it demonstrates a number of interesting Hare features, I thought it would make for a cool blog post. This program must do the following things:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Scan through the file looking for @_IO* constructs&lt;/li&gt;&lt;li&gt;Parse these @_IO* constructs&lt;/li&gt;&lt;li&gt;Determine the size of the type specified by the third parameter&lt;/li&gt;&lt;li&gt;Compute the ioctl number based on these inputs&lt;/li&gt;&lt;li&gt;Write the computed constant to the output&lt;/li&gt;&lt;li&gt;Pass everything else through unmodified&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The implementation begins thusly:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ioctlre&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;typedefre&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@init&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;ioctlre&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;`@(_IO[RW]*)\((.*)\)`&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;typedefre&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;`^(export )?type `&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@fini&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;fini&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ioctlre&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;typedefre&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This sets aside two regular expressions: one that identifies type aliases (so that we can parse them to determine their size later), and one that identifies our @_IO* pseudo-macros. I also defined some types to store each of the details necessary to compute the ioctl assignment:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IOW&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IOR&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IOWR&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IOW&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IOR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;ioctl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;rune&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hare’s standard library includes tools for parsing and analyzing Hare programs in the &lt;a href=&quot;https://docs.harelang.org/hare&quot; target=&quot;_blank&quot;&gt;hare namespace&lt;/a&gt;. We’ll need to use these to work with types in this program. At the start of the program, we initialize a “type store” from hare::types, which provides a mechanism with which Hare types can be processed and stored. The representation of Hare types varies depending on the architecture (for example, pointer types have different sizes on 32-bit and 64-bit systems), so we have to specify the architecture we want. In the future it will be necessary to make this configurable, but for now I just hard-coded x86_64:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x86_64&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;store_free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The two “null” parameters are not going to be used here, but are designed to facilitate evaluating expressions in type definitions, such as &lt;code&gt;[8 * 16]int&lt;/code&gt;. Leaving them null is permissible, but disables the ability to do this sort of thing.&lt;/p&gt;&lt;p&gt;Following this, we enter a loop which processes the input file line-by-line, testing each line against our regular expressions and doing some logic on them if they match. Let’s start with the code for handling new types:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;scanline&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;fromutf8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;typedefre&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;unreadrune&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &amp;apos;&lt;/span&gt;&lt;span class=&quot;error string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;unread&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;toutf8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;loadtype&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// ...to be continued...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we encounter a line which matches our type declaration regular expression, then we unread that line back into the (buffered) standard input stream, then call this “loadtype” function to parse and load it into the type store.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;ha&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;loadtype&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;typestore&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tee&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;tee&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tee&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;lt;ioctl&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;decl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;decl&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Error parsing type declaration:&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;decl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;decl&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;decl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tdecl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;decl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;decl&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;decl_type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tdecl&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Multiple type declarations are unsupported&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tdecl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tdecl&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tdecl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newalias&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tdecl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hare includes a Hare lexer and parser in the standard library, which we’re making use of here. The first thing we do is use &lt;a href=&quot;https://docs.harelang.org/io#tee&quot; target=&quot;_blank&quot;&gt;io::tee&lt;/a&gt; to copy any data the parser reads into stdout, passing it through to the output file. Then we set up a lexer and parse the type declaration. A type declaration looks something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;sock_fprog&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sock_filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The types::lookup call looks up the struct type, and newalias creates a new type alias based on that type with the given name (sock_filter). Adding this to the type store will let us resolve the type when we encounter it later on, for example in this line:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNGETFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;219&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sock_fprog&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back to the main loop, we have another regex test to check if we’re looking at a line with one of these pseudo-macros:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioctlre&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; void &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;capture&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cap&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_IO&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_IOR&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;IOR&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;IOW&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_IOWR&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;IOWR&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fatalf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;Unknown&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioctl&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;direction&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioctl&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;parseioctl&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recall that the regex from earlier is &lt;code&gt;@(_IO[RW]*)\((.*)\)&lt;/code&gt;. This has two capture groups: one for “_IO” or “_IOW” and so on, and another for the list of “parameters” (the zeroth “capture group” is the entire match string). We use the first capture group to grab the ioctl direction, then we pass that into “parseioctl” along with the type store and the second capture group.&lt;/p&gt;&lt;p&gt;This “parseioctl” function is kind of neat:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;parseioctl&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;typestore&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;ioctl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;toutf8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;lt;ioctl&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;LIT_RUNE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;rune&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;COMMA&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;LIT_ICONST&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;i64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;COMMA&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;_type&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Error:&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Error:&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;deferred&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Error: this tool does not support forward references&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;_type&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Error:&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatalf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Error: unexpected {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;tokstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we’ve essentially set up a miniature parser based on a Hare lexer to parse our custom parameter list grammar. We create a &lt;a href=&quot;https://docs.harelang.org/bufio#fixed&quot; target=&quot;_blank&quot;&gt;fixed reader&lt;/a&gt; from the capture group string, then create a lexer based on this and start pulling tokens out of it. The first parameter is a rune, so we grab a LIT_RUNE token and extract the Hare rune value from it, then after a COMMA token we repeat this with LIT_ICONST to get the integer constant. dir::IO ioctls don’t have a type parameter, so can return early in this case.&lt;/p&gt;&lt;p&gt;Otherwise, we use &lt;a href=&quot;https://docs.harelang.org/hare/parse#_type&quot; target=&quot;_blank&quot;&gt;hare::parse::_type&lt;/a&gt; to parse the type parameter, producing a &lt;a href=&quot;https://docs.harelang.org/hare/ast#_type&quot; target=&quot;_blank&quot;&gt;hare::ast::_type&lt;/a&gt;. We then pass this to the type store to look up technical details about this type, such as its size, alignment, storage representation, and so on. This converts the AST type — which only has lexical information — into an actual type, including semantic information about the type.&lt;/p&gt;&lt;p&gt;Equipped with this information, we can calculate the ioctl’s assigned number:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_NRBITS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_TYPEBITS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_SIZEBITS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// XXX: Arch-specific&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_DIRBITS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// XXX: Arch-specific&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_NRSHIFT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_TYPESHIFT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_NRSHIFT&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_NRBITS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_SIZESHIFT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_TYPESHIFT&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_TYPEBITS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_DIRSHIFT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_SIZESHIFT&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_SIZEBITS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;ioctlno&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ioctl&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;typesz&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;_type&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_DIRSHIFT&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_TYPESHIFT&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_NRSHIFT&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;typesz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IOC_SIZESHIFT&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And, back in the main loop, print it to the output:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioctlno&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ioctl&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have successfully converted this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;sock_filter&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;jt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;jf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;sock_fprog&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sock_filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNATTACHFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_IOW&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;T&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;213&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sock_fprog&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Into this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;TUNATTACHFILTER&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x401054d5&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A quick C program verifies our result:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;linux/ioctl.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;linux/if_tun.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;main&lt;/span&gt;() {
	&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;TUNATTACHFILTER: 0x%lx\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;TUNATTACHFILTER&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;TUNATTACHFILTER: 0x401054d5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It works!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Critics may draw attention to the fact that we could have saved ourselves much of this work if Hare had first-class macros, but macros are not aligned with Hare’s design goals, so an alternative solution is called for. This particular program is useful only in a small set of specific circumstances (and mainly for Hare developers themselves, less so for most users), but it solves the problem pretty neatly given the constraints it has to work within.&lt;/p&gt;&lt;p&gt;I think this is a nice case study in a few useful features available from the Hare standard library. In addition to POSIX Extended Regular Expression support via the &lt;a href=&quot;https://docs.harelang.org/regex&quot; target=&quot;_blank&quot;&gt;regex&lt;/a&gt; module, the &lt;a href=&quot;https://docs.harelang.org/hare&quot; target=&quot;_blank&quot;&gt;hare namespace&lt;/a&gt; offers many tools to provide Hare programs with relatively deep insights into the language itself. We can use hare::lex to parse the custom grammar for our pseudo-macros, use hare::parse to parse type declarations, and use hare::types to compute the semantic details of each type. I also like many of the “little things” on display here, such as unreading data back into the buffered stdin reader, or using io::tee to copy data to stdout during parsing.&lt;/p&gt;&lt;p&gt;I hope you found it interesting!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/generating-ioctls/</link>
        
        <pubDate>Sat, 14 May 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/generating-ioctls/</guid>
      </item>
    
      <item>
        
        
          <title>When will we learn?</title>
          <description>
            &lt;p&gt;Congratulations to Rust for its first (but not its last) supply-chain attack this week! They join a growing club of broken-by-design package managers which publish packages uploaded by vendors directly, with no review step, and ship those packages directly to users with no further scrutiny.&lt;/p&gt;&lt;h2&gt;Timeline of major incidents on npm/Crates/PyPI/etc&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;2022-05-10: Cargo: &lt;a href=&quot;https://blog.rust-lang.org/2022/05/10/malicious-crate-rustdecimal.html&quot; target=&quot;_blank&quot;&gt;rustdecimal&lt;/a&gt; ships with malicious code&lt;/li&gt;&lt;li&gt;2022-05-09: npm: &lt;a href=&quot;https://mastodon.social/@lrvick/108274062191145538&quot; target=&quot;_blank&quot;&gt;foreach&lt;/a&gt; is taken over via an expired email domain&lt;/li&gt;&lt;li&gt;2022-03-17: npm: &lt;a href=&quot;https://www.bleepingcomputer.com/news/security/big-sabotage-famous-npm-package-deletes-files-to-protest-ukraine-war/&quot; target=&quot;_blank&quot;&gt;node-ipc&lt;/a&gt; ships malware targeting Russia and Belarus&lt;/li&gt;&lt;li&gt;2022-01-09: npm: &lt;a href=&quot;https://www.bleepingcomputer.com/news/security/dev-corrupts-npm-libs-colors-and-faker-breaking-thousands-of-apps/&quot; target=&quot;_blank&quot;&gt;colors and faker&lt;/a&gt; are deliberately sabotaged&lt;/li&gt;&lt;li&gt;2021-11-19: PyPI: &lt;a href=&quot;https://arstechnica.com/information-technology/2021/11/malware-downloaded-from-pypi-41000-times-was-surprisingly-stealthy/&quot; target=&quot;_blank&quot;&gt;11 malicious packages&lt;/a&gt; discovered&lt;/li&gt;&lt;li&gt;2021-11-04: npm: &lt;a href=&quot;https://github.com/advisories/GHSA-g2q5-5433-rhrf&quot; target=&quot;_blank&quot;&gt;rc&lt;/a&gt; ships malicious code&lt;/li&gt;&lt;li&gt;2021-11-04: npm: &lt;a href=&quot;https://www.bleepingcomputer.com/news/security/popular-coa-npm-library-hijacked-to-steal-user-passwords/&quot; target=&quot;_blank&quot;&gt;coa&lt;/a&gt; steals your passwords&lt;/li&gt;&lt;li&gt;2021-10-22: npm: &lt;a href=&quot;https://github.com/advisories/GHSA-pjwm-rvh2-c87w&quot; target=&quot;_blank&quot;&gt;ua-parser-js&lt;/a&gt; ships malicious code&lt;/li&gt;&lt;li&gt;2021-10-11: PyPI: &lt;a href=&quot;https://twitt.re/maximilianhils/status/1447525552370458625?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1447525552370458625%7Ctwgr%5E%7Ctwcon%5Es1_&amp;ref_url=https%3A%2F%2Fwww.bleepingcomputer.com%2Fnews%2Fsecurity%2Fpypi-removes-mitmproxy2-over-code-execution-concerns%2F&quot; target=&quot;_blank&quot;&gt;mitmproxy2&lt;/a&gt; typo-squats mitmproxy with an added RCE&lt;/li&gt;&lt;li&gt;2021-07-30: PyPI: &lt;a href=&quot;https://arstechnica.com/gadgets/2021/07/malicious-pypi-packages-caught-stealing-developer-data-and-injecting-code/&quot; target=&quot;_blank&quot;&gt;8 malicious packages&lt;/a&gt; discovered&lt;/li&gt;&lt;li&gt;2020-12-16: RubyGems: &lt;a href=&quot;https://blog.sonatype.com/rubygems-laced-with-bitcoin-stealing-malware&quot; target=&quot;_blank&quot;&gt;pretty_color&lt;/a&gt; (and one other) steals bitcoin from victims&lt;/li&gt;&lt;li&gt;2020-09-11: npm: &lt;a href=&quot;https://github.com/advisories?query=https%3A%2F%2Fjs-metrics.com%2Fminjs.php&quot; target=&quot;_blank&quot;&gt;dozens of packages&lt;/a&gt; steal your user’s credit card number&lt;/li&gt;&lt;li&gt;2020-09-03: npm: &lt;a href=&quot;https://github.com/advisories/GHSA-vm6v-w6q2-mrrq&quot; target=&quot;_blank&quot;&gt;bb-builder&lt;/a&gt; steals your password&lt;/li&gt;&lt;li&gt;2020-04-16: RubyGems: &lt;a href=&quot;https://blog.reversinglabs.com/blog/mining-for-malicious-ruby-gems&quot; target=&quot;_blank&quot;&gt;760+ malicious packages&lt;/a&gt; found stealing bitcoin&lt;/li&gt;&lt;li&gt;2018-11-28: npm: &lt;a href=&quot;https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident&quot; target=&quot;_blank&quot;&gt;event-stream&lt;/a&gt; ships with a bitcoin theft kit&lt;/li&gt;&lt;li&gt;2018-10-21: PyPI: &lt;a href=&quot;https://scribe.rip/medium.com/@bertusk/cryptocurrency-clipboard-hijacker-discovered-in-pypi-repository-b66b8a534a8&quot; target=&quot;_blank&quot;&gt;colourama&lt;/a&gt; sneaks bitcoin addresses into your clipboard&lt;/li&gt;&lt;li&gt;2018-10-13: PyPI: &lt;a href=&quot;https://scribe.rip/medium.com/@bertusk/detecting-cyber-attacks-in-the-python-package-index-pypi-61ab2b585c67&quot; target=&quot;_blank&quot;&gt;more typo-squatting malware&lt;/a&gt; attempts various attacks&lt;/li&gt;&lt;li&gt;2018-07-12: npm: &lt;a href=&quot;https://github.com/eslint/eslint-scope/issues/39&quot; target=&quot;_blank&quot;&gt;eslint-scope&lt;/a&gt; ships with malicious code&lt;/li&gt;&lt;li&gt;2018-07-08: AUR: &lt;a href=&quot;https://lists.archlinux.org/pipermail/aur-general/2018-July/034151.html&quot; target=&quot;_blank&quot;&gt;acroread&lt;/a&gt; is compromised&lt;/li&gt;&lt;li&gt;2018-05-11: Snap: &lt;a href=&quot;https://github.com/canonical-web-and-design/snapcraft.io/issues/651&quot; target=&quot;_blank&quot;&gt;a 2048 clone&lt;/a&gt; ships a cryptocurrency miner&lt;/li&gt;&lt;li&gt;2017-09-09: PyPI: &lt;a href=&quot;https://www.nbu.gov.sk/skcsirt-sa-20170909-pypi/&quot; target=&quot;_blank&quot;&gt;typo-squatted packages&lt;/a&gt; published by researchers&lt;/li&gt;&lt;li&gt;2016-07-22: npm: &lt;a href=&quot;https://www.infoworld.com/article/3047177/how-one-yanked-javascript-package-wreaked-havoc.html&quot; target=&quot;_blank&quot;&gt;left-pad&lt;/a&gt; incident&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There are hundreds of additional examples. I had to leave many of them out. &lt;a href=&quot;https://github.com/advisories?query=type%3Areviewed+malicious+package+severity%3Acritical&quot; target=&quot;_blank&quot;&gt;Here’s a good source&lt;/a&gt; if you want to find more.&lt;/p&gt;&lt;h2&gt;Timeline of similar incidents in official Linux distribution repositories&lt;/h2&gt;&lt;div class=&quot;text-center&quot; style=&quot;color: #777; margin: 3rem 0&quot;&gt;
  (this space deliberately left blank)
&lt;/div&gt;
&lt;h2&gt;Why is this happening?&lt;/h2&gt;&lt;p&gt;The correct way to ship packages is with your distribution’s package manager. These have a separate review step, completely side-stepping typo-squatting, establishing a long-term relationship of trust between the vendor and the distribution packagers, and providing a dispassionate third-party to act as an intermediary between users and vendors. Furthermore, they offer stable distributions which can be relied upon for an extended period of time, provide cohesive whole-system integration testing, and unified patch distribution and CVE notifications for your entire system.&lt;/p&gt;&lt;p&gt;For more details, see my previous post, &lt;a href=&quot;https://drewdevault.com/blog/Let-distros-do-their-job/&quot;&gt;Developers: Let distros do their job&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Can these package managers do it better?&lt;/h2&gt;&lt;p&gt;I generally feel that overlay package managers (a term I just made up for npm et al) are redundant. However, you may feel otherwise, and wonder what they could do better to avoid these problems.&lt;/p&gt;&lt;p&gt;It’s simple: they should organize themselves more like a system package manager.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Establish package maintainers independent of the vendors&lt;/li&gt;&lt;li&gt;Establish a review process for package updates&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;There’s many innovations that system package managers have been working on which overlay package managers could stand to learn from as well, such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Universal package signatures and verification&lt;/li&gt;&lt;li&gt;Reproducible builds&lt;/li&gt;&lt;li&gt;Mirrored package distribution&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For my part, I’ll stick to the system package manager. But if you think that the overlay package manager can do it better: prove it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Supply-chain-when-will-we-learn/</link>
        
        <pubDate>Thu, 12 May 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Supply-chain-when-will-we-learn/</guid>
      </item>
    
      <item>
        
        
          <title>Implementing an SSH agent in Hare</title>
          <description>
            &lt;p&gt;&lt;em&gt;Cross-posted from &lt;a href=&quot;https://harelang.org/blog/2022-05-09-hare-ssh/&quot; target=&quot;_blank&quot;&gt;the Hare blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;In the process of writing an SSH agent for &lt;a href=&quot;https://sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;Himitsu&lt;/a&gt;, I needed to implement many SSH primitives from the ground up in Hare, now available via &lt;a href=&quot;https://sr.ht/~sircmpwn/hare-ssh&quot; target=&quot;_blank&quot;&gt;hare-ssh&lt;/a&gt;. Today, I’m going to show you how it works!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: This blog post deals with cryptography-related code. The code you’re going to see today is incomplete, unaudited, and largely hasn’t even seen any code review. Let me begin with a quote from the “crypto” module’s documentation in the Hare standard library:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Cryptography is a difficult, high-risk domain of programming. The life and well-being of your users may depend on your ability to implement cryptographic applications with due care. Please carefully read all of the documentation, double-check your work, and seek second opinions and independent review of your code. Our documentation and API design aims to prevent easy mistakes from being made, but it is no substitute for a good background in applied cryptography.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Do your due diligence before repurposing anything you see here.&lt;/p&gt;&lt;h2&gt;Decoding SSH private keys&lt;/h2&gt;&lt;p&gt;Technically, you do not need to deal with OpenSSH private keys when implementing an SSH agent. However, my particular use-case includes dealing with this format, so I started here. Unlike much of SSH, the OpenSSH private key format (i.e. the format of the file at ~/.ssh/id_ed25519) is, well, private. It’s not documented and I had to get most of the details from reverse-engineering the OpenSSH C code. The main area of interest is sshkey.c. I’ll spare you from reading it yourself and just explain how it works.&lt;/p&gt;&lt;p&gt;First of all, let’s just consider what an SSH private key looks like:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDTIm/zSI
7zeHAs4rIXaOD1AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIE7qq/pMk9VrRupn
9j4/tNHclJnKgJAE1pfUecRNT1fAAAAAoEcx6mnJmFlYXx1eYztw6SZ5yuL6T1LWfj+bpg
7zNQBoqJW1j+Q17PUMtXj9wDDOQx+6OE7JT/RrK3Vltp4oXmFI4FgsYbE9RbNXSC2xvLaX
fplmx+eAOir9UTZGTIbOGy1cVho8LzDLLo4WiGYbpxtIvkJE72f0YdTm8RrNVkLlAy7ayV
uFcoq1JBrjIAa7UtqIr9SG8b76ALJZb9jPc3A=
-----END OPENSSH PRIVATE KEY-----
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can immediately tell that this is a &lt;a href=&quot;https://docs.harelang.org/encoding/pem&quot; target=&quot;_blank&quot;&gt;PEM&lt;/a&gt; file (&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7468&quot; target=&quot;_blank&quot;&gt;RFC 7468&lt;/a&gt;). The first step to read this file was to implement a decoder for the PEM format, which has been on our to-do list for a while now, and is also needed for many other use-cases. Similar to many other formats provided in the standard library, you can call &lt;a href=&quot;https://docs.harelang.org/encoding/pem#newdecoder&quot; target=&quot;_blank&quot;&gt;pem::newdecoder&lt;/a&gt; to create a PEM decoder for an arbitrary I/O source, returning the decoder state on the stack. We can then call &lt;a href=&quot;https://docs.harelang.org/encoding/pem#next&quot; target=&quot;_blank&quot;&gt;pem::next&lt;/a&gt; to find the next PEM header (&lt;code&gt;-----BEGIN...&lt;/code&gt;), which returns a decoder for that specific PEM blob (this design accommodates PEM files which have several PEM segments concatenated together, or intersperse other data in the file alongside the PEM bits. This is common for other PEM use-cases). With this secondary decoder, we can simply read from it like any other I/O source and it decodes the base64-encoded data and returns it to us as bytes.&lt;/p&gt;&lt;p&gt;Based on this, we can examine the contents of this key with a simple program.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newdecoder&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;pemdecoder&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;PEM data &amp;apos;{}&amp;apos;:&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;drain&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;constant variable namespace&quot;&gt;hex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running this program on our sample key yields the following:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;PEM data &amp;apos;OPENSSH PRIVATE KEY&amp;apos;:
00000000  6f 70 65 6e 73 73 68 2d  6b 65 79 2d 76 31 00 00  |openssh-key-v1..|
00000010  00 00 0a 61 65 73 32 35  36 2d 63 74 72 00 00 00  |...aes256-ctr...|
00000020  06 62 63 72 79 70 74 00  00 00 18 00 00 00 10 d3  |.bcrypt.........|
00000030  22 6f f3 48 8e f3 78 70  2c e2 b2 17 68 e0 f5 00  |&amp;quot;o.H..xp,...h...|
00000040  00 00 10 00 00 00 01 00  00 00 33 00 00 00 0b 73  |..........3....s|
00000050  73 68 2d 65 64 32 35 35  31 39 00 00 00 20 4e ea  |sh-ed25519... N.|
00000060  ab fa 4c 93 d5 6b 46 ea  67 f6 3e 3f b4 d1 dc 94  |..L..kF.g.&amp;gt;?....|
00000070  99 ca 80 90 04 d6 97 d4  79 c4 4d 4f 57 c0 00 00  |........y.MOW...|
00000080  00 a0 47 31 ea 69 c9 98  59 58 5f 1d 5e 63 3b 70  |..G1.i..YX_.^c;p|
00000090  e9 26 79 ca e2 fa 4f 52  d6 7e 3f 9b a6 0e f3 35  |.&amp;amp;y...OR.~?....5|
000000a0  00 68 a8 95 b5 8f e4 35  ec f5 0c b5 78 fd c0 30  |.h.....5....x..0|
000000b0  ce 43 1f ba 38 4e c9 4f  f4 6b 2b 75 65 b6 9e 28  |.C..8N.O.k+ue..(|
000000c0  5e 61 48 e0 58 2c 61 b1  3d 45 b3 57 48 2d b1 bc  |^aH.X,a.=E.WH-..|
000000d0  b6 97 7e 99 66 c7 e7 80  3a 2a fd 51 36 46 4c 86  |..~.f...:*.Q6FL.|
000000e0  ce 1b 2d 5c 56 1a 3c 2f  30 cb 2e 8e 16 88 66 1b  |..-\V.&amp;lt;/0.....f.|
000000f0  a7 1b 48 be 42 44 ef 67  f4 61 d4 e6 f1 1a cd 56  |..H.BD.g.a.....V|
00000100  42 e5 03 2e da c9 5b 85  72 8a b5 24 1a e3 20 06  |B.....[.r..$.. .|
00000110  bb 52 da 88 af d4 86 f1  be fa 00 b2 59 6f d8 cf  |.R..........Yo..|
00000120  73 70                                             |sp|
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;OpenSSH private keys begin with a magic string, “openssh-key-v1\0”, which we can see here. Following this are a number of binary encoded fields which are represented in a manner similar to the SSH wire protocol, most often as strings prefixed by their length, encoded as a 32-bit big-endian integer. In order, the fields present here are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cipher name (aes256-ctr)&lt;/li&gt;&lt;li&gt;KDF name (bcrypt)&lt;/li&gt;&lt;li&gt;KDF data&lt;/li&gt;&lt;li&gt;Public key data&lt;/li&gt;&lt;li&gt;Private key data (plus padding)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We parse this information like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;sshprivkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;kdfname&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;kdf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;pubkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;decodesshprivate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sshprivkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pem&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;newdecoder&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;pem&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;pemdecoder&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;OPENSSH PRIVATE KEY&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;magicbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;readall&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;magicbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;magicbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;toutf8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;magic&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;sshprivkey&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cipher&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;kdfname&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;kdf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readslice&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;readall&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;begetu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;nkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;comment spell&quot;&gt;// OpenSSH currently hard-codes the number of keys to 1&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pubkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readslice&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;privkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readslice&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// Add padding bytes&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;drain&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, to get at the actual private key — so that we can do cryptographic operations with it — we first have to decrypt this inner data. Those three fields — cipher name, KDF name, and KDF data — are our hint. In essence, this data is encrypted by OpenSSH by using a variant of &lt;a href=&quot;https://en.wikipedia.org/wiki/Bcrypt&quot; target=&quot;_blank&quot;&gt;bcrypt&lt;/a&gt; as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Key_derivation_function&quot; target=&quot;_blank&quot;&gt;key derivation function&lt;/a&gt;, which turns your password (plus a salt) into a symmetric encryption key. Then it uses &lt;a href=&quot;https://en.wikipedia.org/wiki/Advanced_Encryption_Standard&quot; target=&quot;_blank&quot;&gt;AES 256&lt;/a&gt; in &lt;a href=&quot;https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)&quot; target=&quot;_blank&quot;&gt;CTR mode&lt;/a&gt; with this symmetric key to encrypt the private key data. With the benefit of hindsight, I might question these primitives, but that’s what they use so we’ll have to work with it.&lt;/p&gt;&lt;p&gt;Prior to starting this work, Hare already had support for &lt;a href=&quot;https://docs.harelang.org/crypto/aes&quot; target=&quot;_blank&quot;&gt;AES&lt;/a&gt; and &lt;a href=&quot;https://docs.harelang.org/crypto/cipher#ctr&quot; target=&quot;_blank&quot;&gt;CTR&lt;/a&gt;, though they gained some upgrades during the course of this work, since using an interface for real-world code is the best way to evaluate its design. This leaves us to implement bcrypt.&lt;/p&gt;&lt;p&gt;bcrypt is a password hashing algorithm invented by OpenBSD based on the &lt;a href=&quot;https://en.wikipedia.org/wiki/Blowfish_(cipher)&quot; target=&quot;_blank&quot;&gt;Blowfish&lt;/a&gt; cipher, and it is pretty badly designed. However, Blowfish was fairly straightforward to implement. I’ll spare you the details, but here’s the &lt;a href=&quot;https://docs.harelang.org/crypto/blowfish&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt; and &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare/tree/master/crypto/blowfish&quot; target=&quot;_blank&quot;&gt;implementation&lt;/a&gt; for your consideration. I also implemented the standard bcrypt hash at &lt;a href=&quot;https://docs.harelang.org/crypto/bcrypt&quot; target=&quot;_blank&quot;&gt;crypto::bcrypt&lt;/a&gt;, whose implementation is &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare/tree/master/crypto/bcrypt&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; (for now). This isn’t especially relevant for us, however, since OpenSSH uses a modified form of bcrypt as a key derivation function.&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare-ssh/tree/master/item/format/ssh/bcrypt.ha&quot; target=&quot;_blank&quot;&gt;implementation&lt;/a&gt; the bcrypt KDF in Hare is fairly straightforward. To write it, I referenced OpenSSH portable’s vendored OpenBSD implementation at &lt;code&gt;openbsd-compat/bcrypt_pbkdf.c&lt;/code&gt;, as well as the Go implementation in &lt;code&gt;golang.org/x/crypto&lt;/code&gt;. Then, with these primitives done, we can implement the actual key decryption.&lt;/p&gt;&lt;p&gt;First, not all keys are encrypted with a passphrase, so a simple function tells us if this step is required:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Returns true if this private key is encrypted with a passphrase.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;isencrypted&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sshprivkey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;kdfname&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;none&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “decrypt” function is used to perform the actual decryption. It begins by finding the symmetric key, like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;decrypt&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;sshprivkey&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;pass&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;isencrypted&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;getcipher&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ckey&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;keylen&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;ivlen&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ckey&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ckey&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;kdfbuf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;kdf&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;kdfname&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;bcrypt&amp;quot;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;salt&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;readslice&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;kdfbuf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;salt&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rounds&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;readu32&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;kdfbuf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;bcrypt_pbkdf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ckey&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pass&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;salt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rounds&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;badcipher&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “KDF data” field I mentioned earlier uses a format private to each KDF mode, though at the present time the only supported KDF is this bcrypt one. In this case, it serves as the salt. The “getcipher” function returns some data from a &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare-ssh/tree/master/item/format/ssh/cipher.ha&quot; target=&quot;_blank&quot;&gt;static table of supported ciphers&lt;/a&gt;, which provides us with the required size of the cipher’s key and IV parameters. We allocate sufficient space to store these, create a &lt;a href=&quot;https://docs.harelang.org/bufio#fixed&quot; target=&quot;_blank&quot;&gt;bufio reader&lt;/a&gt; from the KDF field, read out the salt and hashing rounds, and hand all of this over to the bcrypt function to produce our symmetric key (and I/V) in the “ckey” variable.&lt;/p&gt;&lt;p&gt;We may then use these parameters to decrypt the private key area.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;	&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;secretbuf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;secretbuf&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ckey&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;keylen&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ckey&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;keylen&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher_free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;error constant variable&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;readall&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;begetu32&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;4&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;begetu32&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;badpass&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;kdf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;kdfname&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;kdfname&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dup&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dup&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;kdf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “cipher.init” function is an abstraction that allows us to support more ciphers in the future. For this particular cipher mode, it’s implemented fairly simply:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;aes256ctr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ctr_stream&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;aes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ct64_block&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;aes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;CTR_BUFSIZE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;aes256ctr_init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;iv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;aes256ctr&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;aes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;ct64&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;aes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;ct64_init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;st&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;ctr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;iv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within this private key data section, once decrypted, are several fields. First is a random 32-bit integer which is written twice — comparing that these are equal to one another allows us to verify the user’s password. Once verified, we overwrite the private data field in the key structure with the decrypted data, and update the cipher and KDF information to indicate that the key is unencrypted. We could decrypt it directly into the existing private key buffer, without allocating a second buffer, but this would overwrite the encrypted data with garbage if the password was wrong — you’d have to decode the key all over again if the user wants to try again.&lt;/p&gt;&lt;p&gt;So, what does this private key blob look like once decrypted? The hare-ssh repository includes a little program at &lt;code&gt;cmd/sshkey&lt;/code&gt; which dumps all of the information stored in an SSH key, and it provides us with this peek at the private data:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;00000000  fb 15 e6 16 fb 15 e6 16  00 00 00 0b 73 73 68 2d  |............ssh-|
00000010  65 64 32 35 35 31 39 00  00 00 20 4e ea ab fa 4c  |ed25519... N...L|
00000020  93 d5 6b 46 ea 67 f6 3e  3f b4 d1 dc 94 99 ca 80  |..kF.g.&amp;gt;?.......|
00000030  90 04 d6 97 d4 79 c4 4d  4f 57 c0 00 00 00 40 17  |.....y.MOW....@.|
00000040  bf 87 74 0b 2a 74 d5 29  d0 14 10 3f 04 5d 88 c6  |..t.*t.)...?.]..|
00000050  32 fa 21 9c e9 97 b0 5a  e7 7e 5c 02 72 35 72 4e  |2.!....Z.~\.r5rN|
00000060  ea ab fa 4c 93 d5 6b 46  ea 67 f6 3e 3f b4 d1 dc  |...L..kF.g.&amp;gt;?...|
00000070  94 99 ca 80 90 04 d6 97  d4 79 c4 4d 4f 57 c0 00  |.........y.MOW..|
00000080  00 00 0e 73 69 72 63 6d  70 77 6e 40 74 61 69 67  |...sircmpwn@taig|
00000090  61 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f  |a...............|
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see upfront these two 32-bit verification numbers I mentioned, and following this are several fields in a similar format to earlier — length-prefixed strings. The fields are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Key type (“ssh-ed25519” in this case)&lt;/li&gt;&lt;li&gt;Public key (in a format specific to each key type)&lt;/li&gt;&lt;li&gt;Private key (in a format specific to each key type)&lt;/li&gt;&lt;li&gt;Comment&lt;/li&gt;&lt;li&gt;Padding up to the cipher’s block size (16)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is a little bit weird in my opinion — the public key field is redundant with the unencrypted data in this file, and the comment field is probably not so secret as to demand encryption. I think these are just consequences of the file format being private to OpenSSH’s implementation; not much thought has gone into it and implementation details (like the ability to call the same “dump private key” function here as OpenSSH uses elsewhere) have probably leaked through.&lt;/p&gt;&lt;p&gt;We can decode this data with the following Hare code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;decodeprivate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sshprivkey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;isencrypted&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;begetu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;begetu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;badpass&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;keytype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;keytype&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;keytype&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;ssh-ed25519&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;ed25519key&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;decode_ed25519_sk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;comment spell&quot;&gt;// TODO: Support additional key types&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;badcipher&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// An ed25519 key pair.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;ed25519key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;pkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;publickey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;skey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;privatekey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;decode_ed25519_pk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ed25519key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PUBLICKEYSZ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;readall&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pkey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;decode_ed25519_sk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ed25519key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;decode_ed25519_pk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PRIVATEKEYSZ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;readall&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;skey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// Sanity check&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;skey_getpublic&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;skey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pkey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fairly straightforward! Finally, we have extracted the actual private key from the file. For this SSH key, in base64, the cryptographic keys are:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Public key:  Tuqr+kyT1WtG6mf2Pj+00dyUmcqAkATWl9R5xE1PV8A=
Private key: F7+HdAsqdNUp0BQQPwRdiMYy+iGc6ZewWud+XAJyNXJO6qv6TJPVa0bqZ/Y+P7TR3JSZyoCQBNaX1HnETU9XwA==
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Signing and verification with ed25519&lt;/h2&gt;&lt;p&gt;Using these private keys, implementing signatures and signature verification are pretty straightforward. We can stop reading the OpenSSH code at this point — &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8709&quot; target=&quot;_blank&quot;&gt;RFC 8709&lt;/a&gt; standardizes this format for ed25519 signatures.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;crypto&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Signs a message using the provided key, writing the message signature in the&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// SSH format to the provided sink.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;skey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;writestr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;ssh-ed25519&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;writeslice&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Reads an SSH wire signature from the provided I/O handle and verifies that it&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// is a valid signature for the given message and key. If valid, void is&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// returned; otherwise [[badsig]] is returned.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sigtype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sigtype&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sigtype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;keytype&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;badsig&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sig&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readslice&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sig&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sigtype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;ssh-ed25519&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// TODO: other key types&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sig&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SIGNATURESZ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;badsig&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sig&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sig&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SIGNATURESZ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;ed25519&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sig&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;badsig&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implementation writes and reads signatures in the SSH wire format, which is generally how they will be most useful in this context. This code will be expanded in the future with additional keys, such as RSA, once the necessary primitives are implemented for Hare’s standard library.&lt;/p&gt;&lt;h2&gt;The SSH agent protocol&lt;/h2&gt;&lt;p&gt;The agent protocol is also standardized (albeit in draft form), so we refer to &lt;a href=&quot;https://tools.ietf.org/id/draft-miller-ssh-agent-11.html&quot; target=&quot;_blank&quot;&gt;draft-miller-ssh-agent-11&lt;/a&gt; from this point onwards. It’s fairly straightforward. The agent communicates over an unspecified protocol (Unix sockets in practice) by sending messages in the SSH wire format, which, again, mainly comes in the form of strings prefixed by their 32-bit length in network order.&lt;/p&gt;&lt;p&gt;The first step for implementing net::ssh::agent starts with adding types for all of the data structures and enums for all of the constants, which you can find in &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare-ssh/tree/master/item/net/ssh/agent/types.ha&quot; target=&quot;_blank&quot;&gt;types.ha&lt;/a&gt;. Each message begins with its length, then a message type (one byte) and a message payload; the structure of the latter varies with the message type.&lt;/p&gt;&lt;p&gt;I started to approach this by writing some functions which, given a byte buffer that contains an SSH agent message, either parses it or asks for more data.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ln&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;begetu32&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ln&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mtype&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mtype&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;messagetype&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;REQUEST_IDENTITIES&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;request_identities&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;messagetype&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;SIGN_REQUEST&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;parse_sign_request&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;messagetype&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;ADD_IDENTITY&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;parse_add_identity&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// ...trimmed for brevity, and also because it&amp;apos;s full of TODOs...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each individual message payload includes its own parser, except for some messages (such as &lt;code&gt;REQUEST_IDENTITIES&lt;/code&gt;), which have no payload. Here’s what the parser for &lt;code&gt;SIGN_REQUEST&lt;/code&gt; looks like:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;parse_sign_request&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sign_request&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;sign_request&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readslice&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readslice&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;sigflag&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty straightforward! A more complex one is &lt;code&gt;ADD_IDENTITY&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;parse_add_identity&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;add_identity&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;keytype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// TODO: Support more key types&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;keytype&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;ssh-ed25519&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ed25519key&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;npub&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;npub&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pkey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;readall&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pkey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;npriv&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readu32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;npriv&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;skey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;readall&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;skey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;add_identity&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;keytype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;keytype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;readstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One thing I’m not thrilled with in this code is memory management. In Hare, libraries like this one are not supposed to allocate memory if they can get away with it, and if they must, they should do it as conservatively as possible. This implementation does a lot of its own allocations, which is unfortunate. I might refactor it in the future to avoid this. A more subtle issue here is the memory leaks on errors — each of the readslice/readstr functions allocates data for its return value, but if they return an error, the ? operator will return immediately without freeing them. This is a known problem with Hare’s language design, and while we have some ideas for addressing it, we have not completed any of them yet. This is one of a small number of goals for Hare which will likely require language changes prior to 1.0.&lt;/p&gt;&lt;p&gt;We have a &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare-ssh/tree/master/item/net/ssh/agent/agent.ha&quot; target=&quot;_blank&quot;&gt;little bit more code&lt;/a&gt; in net::ssh::agent, which you can check out if you like, but this covers most of it — time to move onto the daemon implementation.&lt;/p&gt;&lt;h2&gt;Completing our SSH agent&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hare-ssh/tree/master/item/cmd/ssh-agent/main.ha&quot; target=&quot;_blank&quot;&gt;ssh-agent&lt;/a&gt; command in the hare-ssh tree is a simple (and non-production) implementation of an SSH agent based on this work. Let’s go over its code to see how this all comes together to make it work.&lt;/p&gt;&lt;p&gt;First, we set up a Unix socket, and somewhere to store our application state.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;pubkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;identities&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;error keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sockpath&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;./socket&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;listener&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sockpath&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;listener&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sockpath&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;chmod&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sockpath&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0o700&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Listening at {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sockpath&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need a main loop, but we need to clean up that Unix socket when we terminate, so we’ll also set up some signal handlers.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;	&lt;span class=&quot;error constant variable&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;SIGINT&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;handle_signal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;SIGTERM&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;handle_signal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// ...stay tuned...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0z&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; i &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;identities&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; 1&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;identities&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;key_finish&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pubkey&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;Terminated&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// ...elsewhere...&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;handle_signal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;sig&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;siginfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;ucontext&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;running&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The actual clean-up is handled by our “defer” statement at the start of “main”. The semantics of signal handling on Unix are complex (and bad), and beyond the scope of this post, so hopefully you already grok them. Our stdlib &lt;a href=&quot;https://docs.harelang.org/unix/signal&quot; target=&quot;_blank&quot;&gt;provides docs&lt;/a&gt;, if you care to learn more, but also includes this warning:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Signal handling is stupidly complicated and easy to get wrong. The standard library makes little effort to help you deal with this. Consult your local man pages, particularly signal-safety(7) on Linux, and perhaps a local priest as well. We advise you to get out of the signal handler as soon as possible, for example via the “self-pipe trick”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;We also provide signalfds on platforms that support them (such as Linux), which is less fraught with issues. Good luck.&lt;/p&gt;&lt;p&gt;Next: the main loop. This code accepts new clients, prepares an agent for them, and hands them off to a second function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;listener&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;interrupted&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;error constant type variable namespace&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;fatalf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;error constant variable&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;agent_finish&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a really simple event loop for a network daemon, and comes with one major limitation: no support for serving multiple clients connecting at once. If you’re curious what a more robust network daemon looks like in Hare, consult the &lt;a href=&quot;https://sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;Himitsu&lt;/a&gt; code.&lt;/p&gt;&lt;p&gt;The “run” function simply reads SSH agent commands and processes them, until the client disconnects.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;readmsg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;message_finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;request_identities&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;handle_req_ident&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;add_identity&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;handle_add_ident&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sign_request&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;handle_sign_request&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;extension_failure&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;writemsg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, this is non-production code, and, among other things, is missing good error handling. The handlers for each message are fairly straightforward, however. Here’s the handler for &lt;code&gt;REQUEST_IDENTITIES&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;handle_req_ident&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;idents&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;identities_answer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;idents&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;identities&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ident&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;identities&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;idents&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;pubkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pubkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;idents&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;writemsg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first one to do something interesting is &lt;code&gt;ADD_IDENTITY&lt;/code&gt;, which allows the user to supply SSH private keys to the agent to work with:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;handle_add_ident&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;add_identity&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sink&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;WRITE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;encode_pubkey&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;identities&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dup&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;privkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;pubkey&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;agent_success&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;writemsg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Added key {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these two messages, we can start to get the agent to do something relatively interesting: accepting and listing keys.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ hare run cmd/ssh-agent/
[2022-05-09 17:39:12] Listening at ./socket
^Z[1]+  Stopped                    hare run cmd/ssh-agent/
$ bg
[1] hare run cmd/ssh-agent/
$ export SSH_AUTH_SOCK=./socket
$ ssh-add -l
The agent has no identities.
$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/sircmpwn/.ssh/id_ed25519: 
Identity added: /home/sircmpwn/.ssh/id_ed25519 (sircmpwn@homura)
2022-05-09 17:39:31] Added key sircmpwn@homura
$ ssh-add -l
256 SHA256:kPr5ZKTNE54TRHGSaanhcQYiJ56zSgcpKeLZw4/myEI sircmpwn@homura (ED25519)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With the last message handler, we can upgrade from something “interesting” to something “useful”:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;handle_sign_request&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sign_request&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;identities&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ident&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;identities&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;pubkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;agent_failure&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;writemsg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;WRITE&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;privkey&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sign_response&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;writemsg&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Signed challenge with key {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For performance reasons, it may be better to use a hash map in a production Hare program (and, as many commenters will be sure to point out, Hare does not provide a built-in hash map or generics). We select the desired key with a linear search, sign the provided payload, and return the signature to the client. Finally, the big pay-off:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ ssh git@git.sr.ht
[2022-05-09 17:41:42] Signed challenge with key sircmpwn@homura
PTY allocation request failed on channel 0
Hi sircmpwn! You&amp;apos;ve successfully authenticated, but I do not provide an interactive shell. Bye!
Connection to git.sr.ht closed.
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Incorporating it into Himitsu&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;Himitsu&lt;/a&gt; was the motivation for all of this work, and I have yet to properly introduce it to the public. I will go into detail later, but in essence, Himitsu is a key-value store that stores some keys in plaintext and some keys encrypted, and acts as a more general form of a password manager. One of the things it can do (at least as of this week) is store your SSH private keys and act as an SSH agent, via a helper called &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu-ssh&quot; target=&quot;_blank&quot;&gt;himitsu-ssh&lt;/a&gt;. The user can import their private key from OpenSSH’s private key format via the “hissh-import” tool, and then the “hissh-agent” daemon provides agent functionality via the Himitsu key store.&lt;/p&gt;&lt;p&gt;The user can import their SSH key like so:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ hissh-import &amp;lt; ~/.ssh/id_ed25519
Enter SSH key passphrase: 
key proto=ssh type=ssh-ed25519 pkey=pF7SljE25sVLdWvInO4gfqpJbbjxI6j+tIUcNWzVTHU= skey! comment=sircmpwn@homura

# Query the key store for keys matching proto=ssh:
$ hiq proto=ssh
proto=ssh type=ssh-ed25519 pkey=pF7SljE25sVLdWvInO4gfqpJbbjxI6j+tIUcNWzVTHU= skey! comment=sircmpwn@homura
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, when running the agent:&lt;/p&gt;&lt;p&gt;&lt;video src=&quot;https://redacted.moe/f/09d422dd.webm&quot; controls muted&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;(Yes, I know that the GUI has issues. I slapped it together in C in an afternoon and it needs a lot of work. &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hiprompt-gtk&quot; target=&quot;_blank&quot;&gt;Help wanted!&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;Ta-da!&lt;/p&gt;&lt;h2&gt;What’s next?&lt;/h2&gt;&lt;p&gt;I accomplished my main goal, which was getting my SSH setup working with Himitsu. The next steps for expanding hare-ssh are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Expanding the supported key types and ciphers (RSA, DSA, etc), which first requires implementing the primitives in the standard library&lt;/li&gt;&lt;li&gt;Implement the SSH connection protocol, which requires primitives like ECDH in the standard library. Some required primitives, like ChaCha, are already supported.&lt;/li&gt;&lt;li&gt;Improve the design of the networking code. hare-ssh is one of a very small number of network-facing Hare libraries, and it’s treading new design ground here.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;SSH is a relatively small target for a cryptography implementation to aim for. I’m looking forward to using it as a testbed for our cryptographic suite. If you’re interested in helping with any of these, &lt;a href=&quot;https://harelang.org/community/&quot; target=&quot;_blank&quot;&gt;please get in touch&lt;/a&gt;! If you’re curious about Hare in general, check out the &lt;a href=&quot;https://harelang.org/tutorials/introduction/&quot; target=&quot;_blank&quot;&gt;language introduction&lt;/a&gt; to get started. Good luck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/hare-ssh/</link>
        
        <pubDate>Mon, 09 May 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/hare-ssh/</guid>
      </item>
    
      <item>
        
        
          <title>Announcing the Hare programming language</title>
          <description>
            &lt;p&gt;The “secret programming language” I have been teasing for several months now is finally here! It is called Hare, and you can read about it on the Hare blog:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://harelang.org/blog/2022-04-25-announcing-hare/&quot; target=&quot;_blank&quot;&gt;https://harelang.org/blog/2022-04-25-announcing-hare/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Check it out!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Announcing-Hare/</link>
        
        <pubDate>Mon, 25 Apr 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Announcing-Hare/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, April 2022</title>
          <description>
            &lt;p&gt;This month marked my first time filing taxes in two countries, and I can assure you it is the worst. I am now a single-issue voter in the US: stop taxing expats! You can get some insight into the financials of SourceHut in the recently-published &lt;a href=&quot;https://sourcehut.org/blog/2022-04-08-2021-financial-report/&quot; target=&quot;_blank&quot;&gt;financial report&lt;/a&gt;. But let’s get right into the fun stuff: free software development news.&lt;/p&gt;&lt;p&gt;There was some slowdown from me this month thanks to all of the business and financial crap I had to put up with, but I was able to get some cool stuff done and many other contributors have been keeping things moving. I’ll start by introducing a new/old project: Himitsu.&lt;/p&gt;&lt;p&gt;Essentially, Himitsu is a secret storage system whose intended use-case is to provide features like password storage and SSH agent functionality. It draws much of its inspiration from Plan 9’s Factotum. You may have stumbled upon an &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu.old&quot; target=&quot;_blank&quot;&gt;early prototype&lt;/a&gt; on git.sr.ht which introduces the basic idea and included the start of an implementation in C. Ultimately I shelved this project for want of a better programming language to implement it with, and then I made a better programming language to implement it with. Over the past two weeks, I have implemented something similar to where the C codebase was left, in fewer than half the lines of code and much less than half the time. Here’s a little peek at what works now:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[12:18:31] taiga ~/s/himitsu $ ./himitsud 
Please enter your passphrase to unlock the keyring: 
[2022-04-15 12:18:56] himitsud running
^Z[1]+  Stopped                    ./himitsud
[12:18:57] taiga ~/s/himitsu $ bg
[1] ./himitsud
[12:18:58] taiga ~/s/himitsu $ nc -U ~/.local/state/himitsu/socket 
add proto=imap host=example.org user=drew@ddevault.org password!=&amp;quot;Hello world!&amp;quot;
^C
[12:19:12] taiga ~/s/himitsu $ ls ~/.local/share/himitsu/
2849c1d5-61b3-4803-98cf-fc57fe5f69a6  index  key
[12:19:14] taiga ~/s/himitsu $ cat ~/.local/share/himitsu/index
YNfVlkORDX1GmXIfL8vOiiTgBJKh47biFsUaKrqzfMP2xfD4B9/lqSl2Y9OtIpVcYzrNjBBZOxcO81vNQdgnvxQ+xaCKaVpQS4Dh6DyaY0/lpq6rfowTY5GwcI155KkmTI4z1ABOVkL4z4XDsQ2DEiqClcQE5/+CxsQ/U/u9DthLJRjrjw==
[12:19:19] taiga ~/s/himitsu $ fg
./himitsud
^C[2022-04-15 12:19:22] himitsud terminated
[12:19:22] taiga ~/s/himitsu $ ./himitsud 
Please enter your passphrase to unlock the keyring: 
Loaded key proto=imap host=example.org user=drew@ddevault.org password!=2849c1d5-61b3-4803-98cf-fc57fe5f69a6
[2022-04-15 12:19:29] himitsud running
^C[2022-04-15 12:19:31] himitsud terminated
[12:19:33] taiga ~/s/himitsu $ find . -type f | xargs wc -l | tail -n1
  895 total
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This project is progressing quite fast and I hope to have it working for some basic use-cases soon. I’ll do a dedicated blog post explaining how it works and why it’s important later on, though it will remain mostly under wraps until the language is released.&lt;/p&gt;&lt;p&gt;Speaking of the language, there were a number of exciting developments this month. Two major standard library initiatives were merged: regex and datetime. The regex implementation is simple, small, and fast, targeting POSIX ERE as a reasonably sane conservative baseline regex dialect. The datetime implementation is quite interesting as well, and it provides a pretty comprehensive API which should address almost all use-cases for timekeeping in our language with a robust and easy-to-use API. As a bonus, and a little flex at how robust our design is, we’ve included support for Martian time. I’m very pleased with how both of these turned out.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;chrono&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;chrono&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;MTC&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Current Martian coordinated time: &amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;STAMP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Other recent improvements include support for signal handling, glob, aes-ni, and net::uri. Work has slowed down on cryptography — please get in touch if you’d like to help. Many readers will be happy to know that there are rumblings about possibly going public soon; after a couple more milestones we’ll be having a meeting to nail down the most urgent priorities before going public and then we’ll get this language into your hands to play with.&lt;/p&gt;&lt;p&gt;I also started a bittorrent daemon in this language, but it’s temporarily blocked until we sort out HTTP/TLS. So, moving right along: SourceHut news? Naturally I will leave most of it for the “what’s cooking” post, but I’ll offer you a little tease of what we’ve been working on: GraphQL. We landed support this month for GraphQL-native webhooks in todo.sr.ht, as well as some new improvements to the pages.sr.ht GQL API. hg.sr.ht is also now starting to see some polish put on its GraphQL support, and some research is underway on GraphQL Federation. Very soon we will be able to put a bow on this work.&lt;/p&gt;&lt;p&gt;That’s all for today! Thanks again for reading and for your ongoing support. I appreciate you!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-April-2022/</link>
        
        <pubDate>Fri, 15 Apr 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-April-2022/</guid>
      </item>
    
      <item>
        
        
          <title>Announcing git snail-mail</title>
          <description>
            &lt;blockquote&gt;&lt;p&gt;You’ve heard of git-over-email thanks to &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;git send-email&lt;/a&gt; — now you can enjoy &lt;em&gt;git snail-mail&lt;/em&gt;: a new tool making it easier than ever to print out git commits on paper and mail them to your maintainers.&lt;/p&gt;&lt;p&gt;Running &lt;code&gt;git snail-mail HEAD~2..&lt;/code&gt; prepares the last two commits for post and sends them directly to the system’s default printer. Configuration options are available for changing printer settings, paper size, and options for faxing or printing envelopes automatically addressed to the maintainers based on address info stored in your git config. Be sure to help the maintainers review your work by including a return envelope and a stamp!&lt;/p&gt;&lt;p&gt;And for maintainers, code review has never been easier — just get out your red marker and write your feedback directly on the patch! When you’re ready to import the patch into your repository, just place it on your scanner and run &lt;code&gt;git scan-mail&lt;/code&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/fa9d0b95.jpg&quot;&gt;
&lt;figcaption&gt;A picture of a patch printed out on paper&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;At least, this is what I’d like to say, but I ended up cancelling the project before it was ready for April Fool’s. After my friend kline (a staffer at Libera Chat) came up with this idea, I actually did write a lot of the code! Git is mostly written in Perl, but I could not really rouse the enthusiasm for implementing this idea in Perl. I did the prototype in $secretlang instead, and got it mostly working, but decided not to try to do some sneaky half-private joke release while trying to maintain the secrecy of the language.&lt;/p&gt;&lt;p&gt;Essentially how it works is this: I have a TeX template for patches:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;tex&quot;&gt;&lt;span class=&quot;keyword_import nospell&quot;&gt;\documentclass&lt;/span&gt;&lt;span class=&quot;string punctuation_bracket nospell&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string nospell&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;string punctuation_bracket nospell&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;keyword_import nospell&quot;&gt;\usepackage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket nospell&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nospell&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;nospell spell&quot;&gt;a4paper&lt;/span&gt;&lt;span class=&quot;nospell&quot;&gt;,
	&lt;/span&gt;&lt;span class=&quot;variable_parameter nospell spell&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;1cm&lt;/span&gt;,
	&lt;span class=&quot;variable_parameter spell nospell&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;1cm&lt;/span&gt;,
	&lt;span class=&quot;variable_parameter spell nospell&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;1cm&lt;/span&gt;,
	&lt;span class=&quot;variable_parameter spell nospell&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;1cm&lt;/span&gt;,
&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;string punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;geometry&lt;/span&gt;&lt;span class=&quot;string punctuation_bracket&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;keyword_import nospell&quot;&gt;\usepackage&lt;/span&gt;&lt;span class=&quot;string punctuation_bracket nospell&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string nospell&quot;&gt;graphicx&lt;/span&gt;&lt;span class=&quot;string punctuation_bracket nospell&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;keyword_import nospell&quot;&gt;\usepackage&lt;/span&gt;&lt;span class=&quot;string punctuation_bracket nospell&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string nospell&quot;&gt;fancyvrb&lt;/span&gt;&lt;span class=&quot;string punctuation_bracket nospell&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;keyword_conditional _name nospell function&quot;&gt;\pagenumbering&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;markup_italic markup_strong markup_heading spell&quot;&gt;gobble&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;module&quot;&gt;\begin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;label nospell spell&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;module&quot;&gt;\section*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;markup_heading_3 spell&quot;&gt;implement os::exec::peek&lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional markup_heading_3 spell function&quot;&gt;\{&lt;/span&gt;,&lt;span class=&quot;markup_heading_3 spell&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional markup_heading_3 spell function&quot;&gt;\}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;spell&quot;&gt;From: Bor Grošelj Simić &lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional spell function&quot;&gt;\textless&lt;/span&gt;&lt;span class=&quot;punctuation_bracket spell&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;punctuation_bracket spell&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;bor.groseljsimic@telemach.net&lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional spell function&quot;&gt;\textgreater&lt;/span&gt;&lt;span class=&quot;punctuation_bracket spell&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;punctuation_bracket spell&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional spell function&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;
Date: Fri&lt;/span&gt;, &lt;span class=&quot;spell&quot;&gt;25 Feb 2022 01:46:13 &lt;/span&gt;&lt;span class=&quot;spell operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;0100

&lt;/span&gt;&lt;span class=&quot;keyword_import spell nospell&quot;&gt;\VerbatimInput&lt;/span&gt;&lt;span class=&quot;string_special_path punctuation_bracket spell nospell&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string_special_path spell nospell&quot;&gt;input.patch&lt;/span&gt;&lt;span class=&quot;string_special_path punctuation_bracket spell nospell&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional spell function&quot;&gt;\newpage&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;
Page 1 of 2 &lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional spell function&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;keyword_import spell nospell&quot;&gt;\includegraphics&lt;/span&gt;&lt;span class=&quot;punctuation_bracket spell nospell&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket spell nospell&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;string_special_path punctuation_bracket spell nospell&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string_special_path spell nospell&quot;&gt;./output-1.png&lt;/span&gt;&lt;span class=&quot;string_special_path punctuation_bracket spell nospell&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional spell function&quot;&gt;\newpage&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;
Page 2 of 2 &lt;/span&gt;&lt;span class=&quot;nospell keyword_conditional spell function&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;spell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;keyword_import spell nospell&quot;&gt;\includegraphics&lt;/span&gt;&lt;span class=&quot;punctuation_bracket spell nospell&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket spell nospell&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;string_special_path punctuation_bracket spell nospell&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string_special_path spell nospell&quot;&gt;./output-2.png&lt;/span&gt;&lt;span class=&quot;string_special_path punctuation_bracket spell nospell&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;module&quot;&gt;\end&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;label nospell spell&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is generated by my git snail-mail code and then run through pdflatex to produce a file &lt;a href=&quot;https://redacted.moe/f/3422cde8.pdf&quot; target=&quot;_blank&quot;&gt;like this&lt;/a&gt;. It pipes it into lp(1) to send it to your printer and ta-da!&lt;/p&gt;&lt;p&gt;I chose not to make the commit selection work like git send-email, because I think that’s one of the most confusing parts of git send-email. Instead I just use a standard revision selection, so to print a single commit, you just name it, and to print a range of commits you use “..”. Here’s a peek at how that works:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;get_commits&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;texdata&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;workdir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;exit_status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fmt&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;`--format=%H%x00%s%x00%aN%x00%aE%x00%aD`&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;git&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;show&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;-s&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;addfile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdout_file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;BUFSIZ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;buffered&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;scanline&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fromutf8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;comment spell&quot;&gt;// XXX: This assumes git always does the thing&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;tokenize&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;\0&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;commit&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;commitdata&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;sha&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next_token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next_token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next_token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next_token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next_token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;constant variable namespace&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;workdir&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;sha&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;diff&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dup&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;commits&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0o644&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;sha&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;^&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;git&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;diff&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;sha&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;addfile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdout_file&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;proc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;--format&lt;/code&gt; argument provided to git at the start here allows me to change the format of git-show to use NUL delimited fields for easily picking out the data I want. Point of note: this is minimum-effort coding for a joke, so there’s a lot of missing error handling and other lazy design choices here.&lt;/p&gt;&lt;p&gt;Anyway, I would have liked to have rewritten this in Perl and pitched it to the git mailing list for inclusion upstream, but alas, after prototyping in $secretlang I could not bring myself to rewrite it in Perl, and the joke fell flat. Not every idea pans out, but they’re still worth trying, anyway. If you want to see some joke projects I’ve made that actually work, check these out:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/shit&quot; target=&quot;_blank&quot;&gt;shit&lt;/a&gt;: a git implementation in POSIX shell&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/bfbot&quot; target=&quot;_blank&quot;&gt;bfbot&lt;/a&gt;: a working IRC bot written in brainfuck&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/classic6&quot; target=&quot;_blank&quot;&gt;classic6&lt;/a&gt;: a working Minecraft server written in 6 hours&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/evilpass&quot; target=&quot;_blank&quot;&gt;evilpass&lt;/a&gt;: a password strength checker that detects password reuse&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/tw&quot; target=&quot;_blank&quot;&gt;tw&lt;/a&gt;: a Wayland compositor for your terminal in 80 lines of “code”&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Take care!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/git-snail-mail/</link>
        
        <pubDate>Fri, 01 Apr 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/git-snail-mail/</guid>
      </item>
    
      <item>
        
        
          <title>It is important for free software to use free software infrastructure</title>
          <description>
            &lt;p&gt;&lt;em&gt;Disclaimer: I founded a project and a company that focuses on free software infrastructure. I will elect not to name them in this post, and will only recommend solutions I do not have a vested interest in.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Free and open source software (FOSS) projects need infrastructure. Somewhere to host the code, to facilitate things like code review, end-user support, bug tracking, marketing, and so on. A common example of this is the “forge” platform: infrastructure which pitches itself as a one-stop shop for many of the needs of FOSS projects in one place, such as code hosting and review, bug tracking, discussions, and so on. Many projects will also reach for additional platforms to provide other kinds of infrastructure: chat rooms, forums, social media, and more.&lt;/p&gt;&lt;p&gt;Many of these needs have &lt;abbr title=&quot;Projects which do not use a license compatible with the Free Software guidelines, i.e. non-FOSS.&quot;&gt;non-free&lt;/abbr&gt;, proprietary solutions available. GitHub is a popular proprietary code forge, and GitLab, the biggest competitor to GitHub, is partially non-free. Some projects use Discord or Slack for chat rooms, Reddit as a forum, or Twitter and Facebook for marketing, outreach, and support; all of these are non-free. In my opinion, relying on these platforms to provide infrastructure for your FOSS project is a mistake.&lt;/p&gt;&lt;p&gt;When your FOSS project chooses to use a non-free platform, you give it an official vote of confidence on behalf of your project. In other words, you lend some of your project’s credibility and legitimacy to the platforms you choose. These platforms are defined by network effects, and your choice is an investment in that network. I would question this investment in and of itself, and the wisdom of offering these platforms your confidence and legitimacy, but there’s a more concerning consequence of this choice as well: an investment in a non-free platform is also a &lt;em&gt;divestment&lt;/em&gt; from the free alternatives.&lt;/p&gt;&lt;p&gt;Again, network effects are the main driver of success in these platforms. Large commercial platforms have a lot of advantages in this respect: large marketing budgets, lots of capital from investors, and the incumbency advantage. The larger the incumbent platform, the more difficult the task of competing with it becomes. Contrast this with free software platforms, which generally don’t have the benefit of large amounts of investment or big marketing budgets. Moreover, businesses are significantly more likely to play dirty to secure their foothold than free software projects are. If your own FOSS projects compete with proprietary commercial options, you should be very familiar with these challenges.&lt;/p&gt;&lt;p&gt;FOSS platforms are at an inherent disadvantage, and your faith in them, or lack thereof, carries a lot of weight. GitHub won’t lose sleep if your project chooses to host its code somewhere else, but choosing &lt;a href=&quot;https://codeberg.org&quot; target=&quot;_blank&quot;&gt;Codeberg&lt;/a&gt;, for example, means a lot to them. In effect, your choice matters disproportionately to the free platforms: choosing GitHub hurts Codeberg much more than choosing Codeberg hurts GitHub. And why should a project choose to use your offering over the proprietary alternatives if you won’t extend them the same courtesy? FOSS solidarity is important for uplifting the ecosystem as a whole.&lt;/p&gt;&lt;p&gt;However, for some projects, what ultimately matters to them has little to do with the benefit of the ecosystem as a whole, but instead considers only the potential for their project’s individual growth and popularity. Many projects choose to prioritize access to the established audience that large commercial platforms provide, in order to maximize their odds of becoming popular, and enjoying some of the knock-on effects of that popularity, such as more contributions.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Such projects would prefer to exacerbate the network effects problem rather than risk some of its social capital on a less popular platform.&lt;/p&gt;&lt;p&gt;To me, this is selfish and unethical outright, though you may have different ethical standards. Unfortunately, arguments against most commercial platforms for any reasonable ethical standard are available in abundance, but they tend to be easily overcome by confirmation bias. Someone who may loudly object to the practices of the US Immigration and Customs Enforcement agency, for example, can quickly find some justification to continue using GitHub despite their collaboration with them. If this example isn’t to your tastes, there are many examples for each of many platforms. For projects that don’t want to move, these are usually swept under the rug.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;But, to be clear, I am not asking you to use inferior platforms for philosophical or altruistic reasons. These are only one of many factors which should contribute to your decision-making, and aptitude is another valid factor to consider. That said, many FOSS platforms are, at least in my opinion, functionally superior to their proprietary competition. Whether their differences are better for your project’s unique needs is something I must leave for you to research on your own, but most projects don’t bother with the research at all. Rest assured: these projects are not ghettos living in the shadow of their larger commercial counterparts, but exciting platforms in their own right which offer many unique advantages.&lt;/p&gt;&lt;p&gt;What’s more, if you need them to do something differently to better suit your project’s needs, you are empowered to improve them. You’re not subservient to the whims of the commercial entity who is responsible for the code, waiting for them to prioritize the issue or even to care about it in the first place. If a problem is important to you, that’s enough for you to get it fixed on a FOSS platform. You might not think you have the time or expertise to do so (though maybe one of your collaborators does), but more importantly, this establishes a mentality of collective ownership and responsibility over all free software as a whole — popularize this philosophy and it could just as easily be you receiving a contribution in a similar manner tomorrow.&lt;/p&gt;&lt;p&gt;In short, choosing non-free platforms is an individualist, short-term investment which prioritizes your project’s apparent access to popularity over the success of the FOSS ecosystem as a whole. On the other hand, choosing FOSS platforms is a collectivist investment in the long-term success of the FOSS ecosystem as a whole, driving its overall growth. Your choice matters. You can help the FOSS ecosystem by choosing FOSS platforms, or you can hurt the FOSS ecosystem by choosing non-free platforms. Please choose carefully.&lt;/p&gt;&lt;p&gt;Here are some recommendations for free software tools that facilitate common needs for free software projects:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Code forges: &lt;a href=&quot;https://codeberg.org&quot; target=&quot;_blank&quot;&gt;Codeberg&lt;/a&gt;, &lt;a href=&quot;https://gitea.io/en-us&quot; target=&quot;_blank&quot;&gt;Gitea&lt;/a&gt;*, &lt;a href=&quot;https://www.gerritcodereview.com&quot; target=&quot;_blank&quot;&gt;Gerrit&lt;/a&gt;*, &lt;a href=&quot;https://gitlab.com&quot; target=&quot;_blank&quot;&gt;GitLab&lt;/a&gt;†&lt;/li&gt;&lt;li&gt;Instant messaging: &lt;a href=&quot;https://matrix.org/&quot; target=&quot;_blank&quot;&gt;Matrix&lt;/a&gt;, &lt;a href=&quot;https://libera.chat&quot; target=&quot;_blank&quot;&gt;Libera Chat&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Publishing: &lt;a href=&quot;https://codeberg.page/&quot; target=&quot;_blank&quot;&gt;Codeberg pages&lt;/a&gt;, &lt;a href=&quot;https://write.as/&quot; target=&quot;_blank&quot;&gt;Write.as&lt;/a&gt;, &lt;a href=&quot;https://joinpeertube.org/&quot; target=&quot;_blank&quot;&gt;PeerTube&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Social media: &lt;a href=&quot;https://joinmastodon.org&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://join-lemmy.org/&quot; target=&quot;_blank&quot;&gt;Lemmy&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Mailing lists: &lt;a href=&quot;https://www.freelists.org&quot; target=&quot;_blank&quot;&gt;FreeLists&lt;/a&gt;, &lt;a href=&quot;https://public-inbox.org/public-inbox-overview.html&quot; target=&quot;_blank&quot;&gt;public-inbox&lt;/a&gt;*, &lt;a href=&quot;http://www.list.org&quot; target=&quot;_blank&quot;&gt;Mailman&lt;/a&gt;*&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;* Self-hosted only &lt;br&gt;† Partially non-free, recommended only if no other solutions are suitable&lt;/p&gt;&lt;p&gt;P.S. If your project is already established on non-free platforms, the easiest time to revisit this choice is right now. It will only ever get more difficult to move as your project grows and gets further established on proprietary platforms. Please consider moving sooner rather than later.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/free-software-free-infrastructure/</link>
        
        <pubDate>Tue, 29 Mar 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/free-software-free-infrastructure/</guid>
      </item>
    
      <item>
        
        
          <title>The Netherlands so far</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://drewdevault.com/2021/06/07/The-Netherlands.html&quot; target=&quot;_blank&quot;&gt;I moved to Amsterdam in July 2021&lt;/a&gt;, and now that I’ve had some time to settle in I thought I’d share my thoughts on how it’s been so far. In short: I love it here!&lt;/p&gt;&lt;p&gt;I did end up finding housing through the hacker community thanks to my earlier post, which was a great blessing. I am renting an apartment from a member of the &lt;a href=&quot;https://techinc.nl&quot; target=&quot;_blank&quot;&gt;Techinc&lt;/a&gt; hacker space, which I have joined as a member myself. One of my biggest fears was establishing a new social network here in the Netherlands, but making friends here has been easy. Through this hacker space and through other connections besides, I have quickly met many wonderful, friendly, and welcoming people, and I have never felt like a stranger in a strange land. For this I am very grateful.&lt;/p&gt;&lt;p&gt;There are many other things to love about this place. One of my favorite things about Amsterdam is getting around by bike. In Philadelphia, travelling by bicycle is signing up for a death wish. In the Netherlands, 27% of all trips utilize a bike, and in Amsterdam it’s as much as 38%. Cyclists enjoy dedicated cycling-first infrastructure, such as bike lanes separated entirely from the roads and dedicated bike-only longer-distance artery roads. The city is designed to reduce points of conflict between bikes and cars, and even when they have to share the road they’re almost always designed to slow cars down and give bikes priority. The whole country is very flat, too, though Dutch people will be quick to tell you about The Hill in their neighborhood, which is always no more than 2 meters tall.&lt;/p&gt;&lt;p&gt;Getting around without a bike is super pleasant as well. I have my choice of bus, tram, metro, long-distance train, or even free ferries across the river, all paid for with the same auto-recharging NFC card for a low price. Every line runs frequent stops, so during the day you’re generally not waiting more than 5 minutes to be picked up and at night you’re probably not going to be waiting more than 15 minutes at popular stops. When it gets really late, though, you might wait as much as 30 minutes. The inter-city trains are amazing — I can show up at any major station without a plan and there’s probably a train heading to where I want to go in less than 10 minutes. Compared to Amtrak, it’s simply mind boggling.&lt;/p&gt;&lt;p&gt;Little things no one here even thinks about have left an impression on me, too. I see street cleaners out all of the time, in a little squad where workers use leaf blowers and brooms to sweep trash and dirt from the sidewalks and squares into the streets where sweepers come through to pick it up. The trash and recycling bins are regularly collected, and when one of them in my neighborhood broke, it was replaced within days. There are some areas where trash does tend to accumulate, though, such as near benches in parks.&lt;/p&gt;&lt;p&gt;Isolated accumulations of trash aside, the parks are great. There’s a lot more of them throughout the city than you’d get in a typical American city. I live close to two large parks, Rembrandtpark and Vondelpark, plus the smaller Erasmuspark, all of which are less than 5 minutes of cycling away. I like to cycle there on cool summer days to read by the lakes or other water features, or on one of the lawns. These parks also typically have a lot of large cycling-only roads which act as little cycling highways throughout the city, which means many of my cycling routes take me through nature even for intra-city travel. Several of the parks also have public gym equipment available, with which you can get a pretty good outdoor work-out for free.&lt;/p&gt;&lt;p&gt;The layout of the neighborhoods is quite nice as well. I have not just one but four grocery stores within walking distance of my house, and I visit one multiple times per week to pick up food, just a 3 or 4 minute walk away from my place. Thanks to the ease of accessing good (and cheap) produce and other ingredients, my diet has improved quite a bit — something I didn’t expect when I moved here. I can’t get everything I want, though: finding genuinely spicy chili peppers is a challenge.&lt;/p&gt;&lt;p&gt;The infamous Dutch bureaucracy is not as bad as people made it out to be. Going through the immigration process was pretty stressful — as any process which could end with being kicked out of the country might be — but it was actually fairly straightforward for the kind of visa I wanted to get. Public servants here are more helpful and flexible than their reputation suggests.&lt;/p&gt;&lt;p&gt;Something which is proving to be a bit of a challenge, however, is learning Dutch. This surprised me given my existing background in languages; I thought it would be pretty easy to pick up. I was able to quickly learn the basics, and I can conduct many everyday affairs in Dutch, but I found it difficult to progress beyond this point with self-study alone. I enrolled in a formal class, which will hopefully help bridge that gap.&lt;/p&gt;&lt;p&gt;I could go on — experiences outside of Amsterdam and throughout the rest of Europe, the vibes of the FOSS community and other communities I’ve met, serendipitously meeting people I knew in America who also moved to Europe, and so on — but I think I’ll stop here for this post. Every time I’ve paused to reflect on my relocation abroad, I’ve come away smiling. So far, so good. Hopefully that doesn’t start to wear off!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Netherlands-update/</link>
        
        <pubDate>Thu, 24 Mar 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Netherlands-update/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, March 2022</title>
          <description>
            &lt;p&gt;Greetings! The weather is starting to warm up again, eh? I’m a bit disappointed that we didn’t get any snow this winter. Yadda yadda insert intro text here. Let’s get down to brass tacks. What’s new this month?&lt;/p&gt;&lt;p&gt;I mainly focused on the programming language this month. I started writing a kernel, which you can see a screenshot of below. This screenshot shows a simulated page fault, demonstrating that we have a working interrupt handler, and also shows something mildly interesting: backtraces. I need to incorporate this approach into the standard library as well, so that we can dump useful stack traces on assertion failures and such. I understand that someone is working on DWARF support as well, so perhaps we’ll soon be able to translate function name + offset into a file name and line number.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/848da9ff.png&quot;&gt;
&lt;figcaption&gt;A redacted screenshot of a kernel showing a simulated page fault&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I also started working on a PNG decoder this weekend, which at the time of writing can successfully decode 77 of the 161 PNG test vectors. I am quite pleased with how the code turned out here: this library is a good demonstration of the strengths of the language. It has simple code which presents a comprehensive interface for the file format, has a strong user-directed memory management model, takes good advantage of features like slices, and makes good use of standard library features like compress::zlib and the I/O abstraction. I will supplement this later with a higher level image API which handles things like pixel format conversions and abstracting away format-specific details.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;zlib&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;idat_reader&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;chunk_reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;inflate&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;zlib&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Returns a new IDAT reader for a [[chunk_reader]], from which raw pixel data&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// may be read via [[io::read]]. The user must prepare a [[decoder]] object&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// along with a working buffer to store the decoder state. For information about&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// preparing a suitable decoder, see [[newdecoder]].&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;new_idat_reader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;chunk_reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;idat_reader&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;ctype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IDAT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Attempted to create IDAT reader for non-IDAT chunk&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;idat_reader&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;st&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;idat_read&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;inflate&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;zlib&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;decompress&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;decoder&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;idat_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ir&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;idat_reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;idat_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buffered&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;decoder_copy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ft&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;scanbyte&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;inflate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;idat_finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ft&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;PAETH&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ft&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// Read one scanline&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;inflate&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// TODO: The rest of the scanline could be in the next&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// IDAT chunk. However, if there is a partially read&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// scanline in the decoder and no IDAT chunk in the&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// remainder of the file, we should probably raise an&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// error.&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;idat_finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;applyfilter&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buffered&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;decoder_copy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dec&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;idat_finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;idat_reader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// Verify checksum&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;comment spell&quot;&gt;// Extra data following zlib stream&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@test&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;idat_reader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;bufio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;no_filtering&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;newreader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;chunk&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;nextchunk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;chunk_reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ihdr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;new_ihdr_reader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ihdr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;ihdr_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ihdr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pixbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;decoder_bufsiz&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ihdr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pixbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;decoder&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;newdecoder&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ihdr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pixbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;chunk&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;nextchunk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;chunk_reader&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;chunk_reader_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;IDAT&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;idat&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;new_idat_reader&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pixels&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;drain&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;idat&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pixels&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pixels&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;no_filtering_data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In SourceHut news, I completed our migration to Alpine 3.15 this month after a brief outage, including an upgrade to our database server, which is upgraded on a less frequent cadance than the others. Thanks to Adnan’s work, we’ve also landed many GraphQL improvements, mainly refactorings and other like improvements, setting the stage for the next series of roll-outs. I plan on transitioning back from focusing on the language to focusing on SourceHut for the coming month, and I expect to see some good progress here.&lt;/p&gt;&lt;p&gt;That’s all I have to share for today. Until next time!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-March-2022/</link>
        
        <pubDate>Tue, 15 Mar 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-March-2022/</guid>
      </item>
    
      <item>
        
        
          <title>It takes a village</title>
          <description>
            &lt;p&gt;As a prolific maintainer of several dozen FOSS projects, I’m often asked how I can get so much done, being just one person. The answer is: I’m not just one person. I have enjoyed the help of thousands of talented people who have contributed to these works. Without them, none of the projects I work on would be successful.&lt;/p&gt;&lt;p&gt;I’d like to take a moment to recognize and thank all of the people who have participated in these endeavours. If you’ve enjoyed any of the projects I’ve worked on, you owe thanks to some of these wonderful people. The following is an incomplete list of authors who have contributed to one or more of the projects I have started:&lt;/p&gt;&lt;p&gt;A Mak&lt;br /&gt; A. M. Joseph&lt;br /&gt; a3v&lt;br /&gt; Aaron Bieber&lt;br /&gt; Aaron Holmes&lt;br /&gt; Aaron Ouellette&lt;br /&gt; Abdelhakim Qbaich&lt;br /&gt; absrd&lt;br /&gt; Ace Eldeib&lt;br /&gt; Adam Kürthy&lt;br /&gt; Adam Mizerski&lt;br /&gt; Aditya Mahajan&lt;br /&gt; Aditya Srivastava&lt;br /&gt; Adnan Maolood&lt;br /&gt; Adrusi&lt;br /&gt; ael-code&lt;br /&gt; agr&lt;br /&gt; Aidan Epstein&lt;br /&gt; Aidan Harris&lt;br /&gt; Ajay R&lt;br /&gt; Ajay Raghavan&lt;br /&gt; Alain Greppin&lt;br /&gt; Aleksa Sarai&lt;br /&gt; Aleksander Usov&lt;br /&gt; Aleksei Bavshin&lt;br /&gt; Aleksis&lt;br /&gt; Alessio&lt;br /&gt; Alex Cordonnier&lt;br /&gt; Alex Maese&lt;br /&gt; Alex McGrath&lt;br /&gt; Alex Roman&lt;br /&gt; alex wennerberg&lt;br /&gt; Alex Xu&lt;br /&gt; Alexander ‘z33ky’ Hirsch&lt;br /&gt; Alexander Bakker&lt;br /&gt; Alexander Dzhoganov&lt;br /&gt; Alexander Harkness&lt;br /&gt; Alexander Johnson&lt;br /&gt; Alexander Taylor&lt;br /&gt; Alexandre Oliveira&lt;br /&gt; Alexey Yerin&lt;br /&gt; Aljaz Gantar&lt;br /&gt; Alynx Zhou&lt;br /&gt; Alyssa Ross&lt;br /&gt; Amin Bandali&lt;br /&gt; amingin&lt;br /&gt; Amir Yalon&lt;br /&gt; Ammar Askar&lt;br /&gt; Ananth Bhaskararaman&lt;br /&gt; Anders&lt;br /&gt; Andreas Rammhold&lt;br /&gt; Andres Erbsen&lt;br /&gt; Andrew Conrad&lt;br /&gt; Andrew Jeffery&lt;br /&gt; Andrew Leap&lt;br /&gt; Andrey Kuznetsov&lt;br /&gt; Andri Yngvason&lt;br /&gt; Andy Dulson&lt;br /&gt; andyleap&lt;br /&gt; Anirudh Oppiliappan&lt;br /&gt; Anjandev Momi&lt;br /&gt; Anthony Super&lt;br /&gt; Anton Gusev&lt;br /&gt; Antonin Décimo&lt;br /&gt; aouelete&lt;br /&gt; apt-ghetto&lt;br /&gt; ARaspiK&lt;br /&gt; Arav K&lt;br /&gt; Ariadna Vigo&lt;br /&gt; Ariadne Conill&lt;br /&gt; Ariel Costas&lt;br /&gt; Ariel Popper&lt;br /&gt; Arkadiusz Hiler&lt;br /&gt; Armaan Bhojwani&lt;br /&gt; Armin Preiml&lt;br /&gt; Armin Weigl&lt;br /&gt; Arnaud Vallette d’Osia&lt;br /&gt; Arsen Arsenović&lt;br /&gt; Art Wild&lt;br /&gt; Arthur Gautier&lt;br /&gt; Arto Jonsson&lt;br /&gt; Arvin Ignaci&lt;br /&gt; ascent12&lt;br /&gt; asdfjkluiop&lt;br /&gt; Asger Hautop Drewsen&lt;br /&gt; ash lea&lt;br /&gt; Ashkan Kiani&lt;br /&gt; Ashton Kemerling&lt;br /&gt; athrungithub&lt;br /&gt; Atnanasi&lt;br /&gt; Aviv Eyal&lt;br /&gt; ayaka&lt;br /&gt; azarus&lt;br /&gt; bacardi55&lt;br /&gt; barfoo1&lt;br /&gt; Bart Pelle&lt;br /&gt; Bart Post&lt;br /&gt; Bartłomiej Burdukiewicz&lt;br /&gt; bbielsa&lt;br /&gt; BearzRobotics&lt;br /&gt; Ben Boeckel&lt;br /&gt; Ben Brown&lt;br /&gt; Ben Burwell&lt;br /&gt; Ben Challenor&lt;br /&gt; Ben Cohen&lt;br /&gt; Ben Fiedler&lt;br /&gt; Ben Harris&lt;br /&gt; Benjamin Cheng&lt;br /&gt; Benjamin Halsted&lt;br /&gt; Benjamin Lowry&lt;br /&gt; Benjamin Riefenstahl&lt;br /&gt; Benoit Gschwind&lt;br /&gt; berfr&lt;br /&gt; bilgorajskim&lt;br /&gt; Bill Doyle&lt;br /&gt; Birger Schacht&lt;br /&gt; Bjorn Neergaard&lt;br /&gt; Björn Esser&lt;br /&gt; blha303&lt;br /&gt; bn4t&lt;br /&gt; Bob Ham&lt;br /&gt; bobtwinkles&lt;br /&gt; boos1993&lt;br /&gt; Bor Grošelj Simić&lt;br /&gt; boringcactus&lt;br /&gt; Brandon Dowdy&lt;br /&gt; BrassyPanache&lt;br /&gt; Brendan Buono&lt;br /&gt; Brendon Smith&lt;br /&gt; Brian Ashworth&lt;br /&gt; Brian Clemens&lt;br /&gt; Brian McKenna&lt;br /&gt; Bruno Pinto&lt;br /&gt; bschacht&lt;br /&gt; BTD Master&lt;br /&gt; buffet&lt;br /&gt; burrowing-owl&lt;br /&gt; Byron Torres&lt;br /&gt; calcdude84se&lt;br /&gt; Caleb Bassi&lt;br /&gt; Callum Brown&lt;br /&gt; Calvin Lee&lt;br /&gt; Cameron Nemo&lt;br /&gt; camoz&lt;br /&gt; Campbell Vertesi&lt;br /&gt; Cara Salter&lt;br /&gt; Carlo Abelli&lt;br /&gt; Cassandra McCarthy&lt;br /&gt; Cedric Sodhi&lt;br /&gt; Chang Liu&lt;br /&gt; Charles E. Lehner&lt;br /&gt; Charlie Stanton&lt;br /&gt; Charmander&lt;br /&gt; chickendude&lt;br /&gt; chr0me&lt;br /&gt; Chris Chamberlain&lt;br /&gt; Chris Kinniburgh&lt;br /&gt; Chris Morgan&lt;br /&gt; Chris Morris&lt;br /&gt; Chris Vittal&lt;br /&gt; Chris Waldon&lt;br /&gt; Chris Young&lt;br /&gt; Christoph Gysin&lt;br /&gt; Christopher M. Riedl&lt;br /&gt; Christopher Vittal&lt;br /&gt; chtison&lt;br /&gt; Clar Charr&lt;br /&gt; Clayton Craft&lt;br /&gt; Clément Joly&lt;br /&gt; cnt0&lt;br /&gt; coderkun&lt;br /&gt; Cole Helbling&lt;br /&gt; Cole Mickens&lt;br /&gt; columbarius&lt;br /&gt; comex&lt;br /&gt; Connor Edwards&lt;br /&gt; Connor Kuehl&lt;br /&gt; Conrad Hoffmann&lt;br /&gt; Cormac Stephenson&lt;br /&gt; Cosimo Cecchi&lt;br /&gt; cra0zy&lt;br /&gt; crondog&lt;br /&gt; Cuber&lt;br /&gt; curiousleo&lt;br /&gt; Cyril Levis&lt;br /&gt; Cédric Bonhomme&lt;br /&gt; Cédric Cabessa&lt;br /&gt; Cédric Hannotier&lt;br /&gt; D.B&lt;br /&gt; dabio&lt;br /&gt; Dacheng Gao&lt;br /&gt; Damien Tardy-Panis&lt;br /&gt; Dan ELKOUBY&lt;br /&gt; Dan Robertson&lt;br /&gt; Daniel Bridges&lt;br /&gt; Daniel De Graaf&lt;br /&gt; Daniel Eklöf&lt;br /&gt; Daniel Gröber&lt;br /&gt; Daniel Kessler&lt;br /&gt; Daniel Kondor&lt;br /&gt; Daniel Lockyer&lt;br /&gt; Daniel Lublin&lt;br /&gt; Daniel Martí&lt;br /&gt; Daniel Otero&lt;br /&gt; Daniel P&lt;br /&gt; Daniel Sockwell&lt;br /&gt; Daniel V&lt;br /&gt; Daniel V.&lt;br /&gt; Daniel Vidmar&lt;br /&gt; Daniel Xu&lt;br /&gt; Daniil&lt;br /&gt; Danilo&lt;br /&gt; Danilo Spinella&lt;br /&gt; Danny Bautista&lt;br /&gt; Dark Rift&lt;br /&gt; Darksun&lt;br /&gt; Dave Cottlehuber&lt;br /&gt; David Arnold&lt;br /&gt; David Blajda&lt;br /&gt; David Eklov&lt;br /&gt; David Florness&lt;br /&gt; David Hurst&lt;br /&gt; David Kraeutmann&lt;br /&gt; David Krauser&lt;br /&gt; David Zero&lt;br /&gt; David96&lt;br /&gt; db&lt;br /&gt; dbandstra&lt;br /&gt; dece&lt;br /&gt; delthas&lt;br /&gt; Denis Doria&lt;br /&gt; Denis Laxalde&lt;br /&gt; Dennis Fischer&lt;br /&gt; Dennis Schridde&lt;br /&gt; Derek Smith&lt;br /&gt; Devin J. Pohly&lt;br /&gt; Devon Johnson&lt;br /&gt; Dhruvin Gandhi&lt;br /&gt; Di Ma&lt;br /&gt; Dian M Fay&lt;br /&gt; Diane&lt;br /&gt; Diederik de Haas&lt;br /&gt; Dillen Meijboom&lt;br /&gt; Dimitris Triantafyllidis&lt;br /&gt; Dizigma&lt;br /&gt; Dmitri Kourennyi&lt;br /&gt; Dmitry Borodaenko&lt;br /&gt; Dmitry Kalinkin&lt;br /&gt; dogwatch&lt;br /&gt; Dominik Honnef&lt;br /&gt; Dominique Martinet&lt;br /&gt; Donnie West&lt;br /&gt; Dorota Czaplejewicz&lt;br /&gt; dudemanguy&lt;br /&gt; Dudemanguy911&lt;br /&gt; Duncaen&lt;br /&gt; Dylan Araps&lt;br /&gt; earnest ma&lt;br /&gt; Ed Younis&lt;br /&gt; EdOverflow&lt;br /&gt; EIREXE&lt;br /&gt; Ejez&lt;br /&gt; Ekaterina Vaartis&lt;br /&gt; Eli Schwartz&lt;br /&gt; Elias Naur&lt;br /&gt; Eloi Rivard&lt;br /&gt; elumbella&lt;br /&gt; Elyes HAOUAS&lt;br /&gt; Elyesa&lt;br /&gt; emersion&lt;br /&gt; Emerson Ferreira&lt;br /&gt; Emmanuel Gil Peyrot&lt;br /&gt; Enerccio&lt;br /&gt; Erazem Kokot&lt;br /&gt; Eric Bower&lt;br /&gt; Eric Drechsel&lt;br /&gt; Eric Engestrom&lt;br /&gt; Eric Molitor&lt;br /&gt; Erik Reider&lt;br /&gt; ernierasta&lt;br /&gt; espkk&lt;br /&gt; Ethan Lee&lt;br /&gt; Euan Torano&lt;br /&gt; EuAndreh&lt;br /&gt; Evan Allrich&lt;br /&gt; Evan Hanson&lt;br /&gt; Evan Johnston&lt;br /&gt; Evan Relf&lt;br /&gt; Eyal Sawady&lt;br /&gt; Ezra&lt;br /&gt; Fabian Geiselhart&lt;br /&gt; Fabio Alessandro Locati&lt;br /&gt; Falke Carlsen&lt;br /&gt; Fazlul Shahriar&lt;br /&gt; Felipe Cardoso Resende&lt;br /&gt; Fenveireth&lt;br /&gt; Ferdinand Bachmann&lt;br /&gt; FICTURE7&lt;br /&gt; Filip Sandborg&lt;br /&gt; finley&lt;br /&gt; Flakebi&lt;br /&gt; Florent de Lamotte&lt;br /&gt; florian.weigelt&lt;br /&gt; Francesco Gazzetta&lt;br /&gt; Francis Dinh&lt;br /&gt; Frank Smit&lt;br /&gt; Franklin “Snaipe” Mathieu&lt;br /&gt; Frantisek Fladung&lt;br /&gt; François Kooman&lt;br /&gt; Frode Aannevik&lt;br /&gt; frsfnrrg&lt;br /&gt; ftilde&lt;br /&gt; fwsmit&lt;br /&gt; Gabriel Augendre&lt;br /&gt; Gabriel Féron&lt;br /&gt; gabrielpatzleiner&lt;br /&gt; Galen Abell&lt;br /&gt; Garrison Taylor&lt;br /&gt; Gauvain ‘GovanifY’ Roussel-Tarbouriech&lt;br /&gt; Gaël PORTAY&lt;br /&gt; gbear605&lt;br /&gt; Genki Sky&lt;br /&gt; Geoff Greer&lt;br /&gt; Geoffrey Casper&lt;br /&gt; George Craggs&lt;br /&gt; George Hilliard&lt;br /&gt; ggrote&lt;br /&gt; Gianluca Arbezzano&lt;br /&gt; gilbus&lt;br /&gt; gildarts&lt;br /&gt; Giuseppe Lumia&lt;br /&gt; Gokberk Yaltirakli&lt;br /&gt; Graham Christensen&lt;br /&gt; Greg Anders&lt;br /&gt; Greg Depoire–Ferrer&lt;br /&gt; Greg Farough&lt;br /&gt; Greg Hewgill&lt;br /&gt; Greg V&lt;br /&gt; Gregory Anders&lt;br /&gt; Gregory Mullen&lt;br /&gt; grossws&lt;br /&gt; Grégoire Delattre&lt;br /&gt; Guido Cella&lt;br /&gt; Guido Günther&lt;br /&gt; Guillaume Brogi&lt;br /&gt; Guillaume J. Charmes&lt;br /&gt; György Kurucz&lt;br /&gt; Gökberk Yaltıraklı&lt;br /&gt; Götz Christ&lt;br /&gt; Haelwenn (lanodan) Monnier&lt;br /&gt; Half-Shot&lt;br /&gt; Hans Brigman&lt;br /&gt; Haowen Liu&lt;br /&gt; Harish Krupo&lt;br /&gt; Harry Jeffery&lt;br /&gt; Heghedus Razvan&lt;br /&gt; Heiko Carrasco&lt;br /&gt; heitor&lt;br /&gt; Henrik Riomar&lt;br /&gt; Honza Pokorny&lt;br /&gt; Hoolean&lt;br /&gt; Hristo Venev&lt;br /&gt; Hubert Hirtz&lt;br /&gt; hugbubby&lt;br /&gt; Hugo Osvaldo Barrera&lt;br /&gt; Humm&lt;br /&gt; Hummer12007&lt;br /&gt; Ian Fan&lt;br /&gt; Ian Huang&lt;br /&gt; Ian Moody&lt;br /&gt; Ignas Kiela&lt;br /&gt; Igor Sviatniy&lt;br /&gt; Ihor Kalnytskyi&lt;br /&gt; Ilia Bozhinov&lt;br /&gt; Ilia Mirkin&lt;br /&gt; Ilja Kocken&lt;br /&gt; Ilya Lukyanov&lt;br /&gt; Ilya Trukhanov&lt;br /&gt; inwit&lt;br /&gt; io mintz&lt;br /&gt; Isaac Freund&lt;br /&gt; Issam E. Maghni&lt;br /&gt; Issam Maghni&lt;br /&gt; István Donkó&lt;br /&gt; Ivan Chebykin&lt;br /&gt; Ivan Fedotov&lt;br /&gt; Ivan Habunek&lt;br /&gt; Ivan Mironov&lt;br /&gt; Ivan Molodetskikh&lt;br /&gt; Ivan Tham&lt;br /&gt; Ivoah&lt;br /&gt; ixru&lt;br /&gt; j-n-f&lt;br /&gt; Jaanus Torp&lt;br /&gt; Jack Byrne&lt;br /&gt; jack gleeson&lt;br /&gt; Jacob Young&lt;br /&gt; jajo-11&lt;br /&gt; Jake Bauer&lt;br /&gt; Jakub Kopański&lt;br /&gt; Jakub Kądziołka&lt;br /&gt; Jamelly Ferreira&lt;br /&gt; James D. Marble&lt;br /&gt; James Edwards-Jones&lt;br /&gt; James Mills&lt;br /&gt; James Murphy&lt;br /&gt; James Pond&lt;br /&gt; James Rowe&lt;br /&gt; James Turner&lt;br /&gt; Jan Beich&lt;br /&gt; Jan Chren&lt;br /&gt; Jan Palus&lt;br /&gt; Jan Pokorný&lt;br /&gt; Jan Staněk&lt;br /&gt; JanUlrich&lt;br /&gt; Jared Baldridge&lt;br /&gt; Jarkko Oranen&lt;br /&gt; Jasen Borisov&lt;br /&gt; Jason Francis&lt;br /&gt; Jason Miller&lt;br /&gt; Jason Nader&lt;br /&gt; Jason Phan&lt;br /&gt; Jason Swank&lt;br /&gt; jasperro&lt;br /&gt; Jayce Fayne&lt;br /&gt; jdiez17&lt;br /&gt; Jeff Kaufman&lt;br /&gt; Jeff Martin&lt;br /&gt; Jeff Peeler&lt;br /&gt; Jeffas&lt;br /&gt; Jelle Besseling&lt;br /&gt; Jente Hidskes&lt;br /&gt; Jeremy Hofer&lt;br /&gt; Jerzi Kaminsky&lt;br /&gt; JerziKaminsky&lt;br /&gt; Jesin&lt;br /&gt; jhalmen&lt;br /&gt; Jiri Vlasak&lt;br /&gt; jman&lt;br /&gt; Joe Jenne&lt;br /&gt; johalun&lt;br /&gt; Johan Bjäreholt&lt;br /&gt; Johannes Lundberg&lt;br /&gt; Johannes Schramm&lt;br /&gt; John Axel Eriksson&lt;br /&gt; John Chadwick&lt;br /&gt; John Chen&lt;br /&gt; John Mako&lt;br /&gt; john muhl&lt;br /&gt; Jon Higgs&lt;br /&gt; Jonas Große Sundrup&lt;br /&gt; Jonas Hohmann&lt;br /&gt; Jonas Kalderstam&lt;br /&gt; Jonas Karlsson&lt;br /&gt; Jonas Mueller&lt;br /&gt; Jonas Platte&lt;br /&gt; Jonathan Bartlett&lt;br /&gt; Jonathan Buch&lt;br /&gt; Jonathan Halmen&lt;br /&gt; Jonathan Schleußer&lt;br /&gt; JonnyMako&lt;br /&gt; Joona Romppanen&lt;br /&gt; Joram Schrijver&lt;br /&gt; Jorge Maldonado Ventura&lt;br /&gt; Jose Diez&lt;br /&gt; Josef Gajdusek&lt;br /&gt; Josh Holland&lt;br /&gt; Josh Junon&lt;br /&gt; Josh Shone&lt;br /&gt; Joshua Ashton&lt;br /&gt; Josip Janzic&lt;br /&gt; José Expósito&lt;br /&gt; José Mota&lt;br /&gt; JR Boyens&lt;br /&gt; Juan Picca&lt;br /&gt; Julian P Samaroo&lt;br /&gt; Julian Samaroo&lt;br /&gt; Julien Moutinho&lt;br /&gt; Julien Olivain&lt;br /&gt; Julien Savard&lt;br /&gt; Julio Galvan&lt;br /&gt; Julius Michaelis&lt;br /&gt; Justin Kelly&lt;br /&gt; Justin Mayhew&lt;br /&gt; Justin Nesselrotte&lt;br /&gt; Justus Rossmeier&lt;br /&gt; Jøhannes Lippmann&lt;br /&gt; k1nkreet&lt;br /&gt; Kacper Kołodziej&lt;br /&gt; Kaleb Elwert&lt;br /&gt; kaltinril&lt;br /&gt; Kalyan Sriram&lt;br /&gt; Karl Rieländer&lt;br /&gt; Karmanyaah Malhotra&lt;br /&gt; Karol Kosek&lt;br /&gt; Kenny Levinsen&lt;br /&gt; kevin&lt;br /&gt; Kevin Hamacher&lt;br /&gt; Kevin Kuehler&lt;br /&gt; Kevin Sangeelee&lt;br /&gt; Kiril Vladimiroff&lt;br /&gt; Kirill Chibisov&lt;br /&gt; Kirill Primak&lt;br /&gt; Kiëd Llaentenn&lt;br /&gt; KoffeinFlummi&lt;br /&gt; Koni Marti&lt;br /&gt; Konrad Beckmann&lt;br /&gt; Konstantin Kharlamov&lt;br /&gt; Konstantin Pospelov&lt;br /&gt; Konstantinos Feretos&lt;br /&gt; kst&lt;br /&gt; Kurt Kartaltepe&lt;br /&gt; Kurt Kremitzki&lt;br /&gt; kushal&lt;br /&gt; Kévin Le Gouguec&lt;br /&gt; Lane Surface&lt;br /&gt; Langston Barrett&lt;br /&gt; Lars Hagström&lt;br /&gt; Laurent Bonnans&lt;br /&gt; Lauri&lt;br /&gt; lbonn&lt;br /&gt; Leon Henrik Plickat&lt;br /&gt; Leszek Cimała&lt;br /&gt; Liam Cottam&lt;br /&gt; Linus Heckemann&lt;br /&gt; Lio Novelli&lt;br /&gt; ljedrz&lt;br /&gt; Louis Taylor&lt;br /&gt; Lubomir Rintel&lt;br /&gt; Luca Weiss&lt;br /&gt; Lucas F. Souza&lt;br /&gt; Lucas M. Dutra&lt;br /&gt; Ludovic Chabant&lt;br /&gt; Ludvig Michaelsson&lt;br /&gt; Lukas Lihotzki&lt;br /&gt; Lukas Märdian&lt;br /&gt; Lukas Wedeking&lt;br /&gt; Lukas Werling&lt;br /&gt; Luke Drummond&lt;br /&gt; Luminarys&lt;br /&gt; Luna Nieves&lt;br /&gt; Lyle Hanson&lt;br /&gt; Lyndsy Simon&lt;br /&gt; Lyudmil Angelov&lt;br /&gt; M Stoeckl&lt;br /&gt; M. David Bennett&lt;br /&gt; Mack Straight&lt;br /&gt; madblobfish&lt;br /&gt; manio143&lt;br /&gt; Manuel Argüelles&lt;br /&gt; Manuel Mendez&lt;br /&gt; Manuel Stoeckl&lt;br /&gt; Marc Grondin&lt;br /&gt; Marcel Hellwig&lt;br /&gt; Marcin Cieślak&lt;br /&gt; Marco Sirabella&lt;br /&gt; Marian Dziubiak&lt;br /&gt; Marien Zwart&lt;br /&gt; Marius Orcsik&lt;br /&gt; Mariusz Bialonczyk&lt;br /&gt; Mark Dain&lt;br /&gt; Mark Stosberg&lt;br /&gt; Markus Ongyerth&lt;br /&gt; MarkusVolk&lt;br /&gt; Marten Ringwelski&lt;br /&gt; Martijn Braam&lt;br /&gt; Martin Dørum&lt;br /&gt; Martin Hafskjold Thoresen&lt;br /&gt; Martin Kalchev&lt;br /&gt; Martin Michlmayr&lt;br /&gt; Martin Vahlensieck&lt;br /&gt; Matias Lang&lt;br /&gt; Matrefeytontias&lt;br /&gt; matrefeytontias&lt;br /&gt; Matt Coffin&lt;br /&gt; Matt Critchlow&lt;br /&gt; Matt Keeter&lt;br /&gt; Matt Singletary&lt;br /&gt; Matt Snider&lt;br /&gt; Matthew Jorgensen&lt;br /&gt; Matthias Beyer&lt;br /&gt; Matthias Totschnig&lt;br /&gt; Mattias Eriksson&lt;br /&gt; Matías Lang&lt;br /&gt; Max Bruckner&lt;br /&gt; Max Leiter&lt;br /&gt; Maxime “pep” Buquet&lt;br /&gt; mbays&lt;br /&gt; MC42&lt;br /&gt; meak&lt;br /&gt; Mehdi Sadeghi&lt;br /&gt; Mendel E&lt;br /&gt; Merlin Büge&lt;br /&gt; Miccah Castorina&lt;br /&gt; Michael Anckaert&lt;br /&gt; Michael Aquilina&lt;br /&gt; Michael Forney&lt;br /&gt; Michael Struwe&lt;br /&gt; Michael Vetter&lt;br /&gt; Michael Weiser&lt;br /&gt; Michael Weiss&lt;br /&gt; Michaël Defferrard&lt;br /&gt; Michał Winiarski&lt;br /&gt; Michel Ganguin&lt;br /&gt; Michele Finotto&lt;br /&gt; Michele Sorcinelli&lt;br /&gt; Mihai Coman&lt;br /&gt; Mikkel Oscar Lyderik&lt;br /&gt; Mikkel Oscar Lyderik Larsen&lt;br /&gt; Milkey Mouse&lt;br /&gt; minus&lt;br /&gt; Mitchell Kutchuk&lt;br /&gt; mliszcz&lt;br /&gt; mntmn&lt;br /&gt; mnussbaum&lt;br /&gt; Moelf&lt;br /&gt; morganamilo&lt;br /&gt; Moritz Buhl&lt;br /&gt; Mrmaxmeier&lt;br /&gt; mteyssier&lt;br /&gt; Mukundan314&lt;br /&gt; muradm&lt;br /&gt; murray&lt;br /&gt; Mustafa Abdul-Kader&lt;br /&gt; mwenzkowski&lt;br /&gt; myfreeweb&lt;br /&gt; Mykola Orliuk&lt;br /&gt; Mykyta Holubakha&lt;br /&gt; n3rdopolis&lt;br /&gt; Naglis Jonaitis&lt;br /&gt; Nate Dobbins&lt;br /&gt; Nate Guerin&lt;br /&gt; Nate Ijams&lt;br /&gt; Nate Symer&lt;br /&gt; Nathan Rossi&lt;br /&gt; Nedzad Hrnjica&lt;br /&gt; NeKit&lt;br /&gt; nerdopolis&lt;br /&gt; ngenisis&lt;br /&gt; Nguyễn Gia Phong&lt;br /&gt; Niccolò Scatena&lt;br /&gt; Nicholas Bering&lt;br /&gt; Nick Diego Yamane&lt;br /&gt; Nick Paladino&lt;br /&gt; Nick White&lt;br /&gt; Nicklas Warming Jacobsen&lt;br /&gt; Nicolai Dagestad&lt;br /&gt; Nicolas Braud-Santoni&lt;br /&gt; Nicolas Cornu&lt;br /&gt; Nicolas Reed&lt;br /&gt; Nicolas Schodet&lt;br /&gt; Nicolas Werner&lt;br /&gt; NightFeather&lt;br /&gt; Nihil Pointer&lt;br /&gt; Niklas Schulze&lt;br /&gt; Nils ANDRÉ-CHANG&lt;br /&gt; Nils Schulte&lt;br /&gt; Nixon Enraght-Moony&lt;br /&gt; Noah Altunian&lt;br /&gt; Noah Kleiner&lt;br /&gt; Noah Loomans&lt;br /&gt; Noah Pederson&lt;br /&gt; Noam Preil&lt;br /&gt; Noelle Leigh&lt;br /&gt; NokiDev&lt;br /&gt; Nolan Prescott&lt;br /&gt; Nomeji&lt;br /&gt; Novalinium&lt;br /&gt; novenary&lt;br /&gt; np511&lt;br /&gt; nrechn&lt;br /&gt; NSDex&lt;br /&gt; Nuew&lt;br /&gt; nyorain&lt;br /&gt; nytpu&lt;br /&gt; Nícolas F. R. A. Prado&lt;br /&gt; oharaandrew314&lt;br /&gt; Oleg Kuznetsov&lt;br /&gt; Oliver Leaver-Smith&lt;br /&gt; oliver-giersch&lt;br /&gt; Olivier Fourdan&lt;br /&gt; Ondřej Fiala&lt;br /&gt; Orestis Floros&lt;br /&gt; Oscar Cowdery Lack&lt;br /&gt; Ossi Ahosalmi&lt;br /&gt; Owen Johnson&lt;br /&gt; Paco Esteban&lt;br /&gt; Parasrah&lt;br /&gt; Pascal Pascher&lt;br /&gt; Patrick Sauter&lt;br /&gt; Patrick Steinhardt&lt;br /&gt; Paul Fenwick&lt;br /&gt; Paul Ouellette&lt;br /&gt; Paul Riou&lt;br /&gt; Paul Spooren&lt;br /&gt; Paul W. Rankin&lt;br /&gt; Paul Wise&lt;br /&gt; Pedro Côrte-Real&lt;br /&gt; Pedro L. Ramos&lt;br /&gt; Pedro Lucas Porcellis&lt;br /&gt; Peroalane&lt;br /&gt; Peter Grayson&lt;br /&gt; Peter Lamby&lt;br /&gt; Peter Rice&lt;br /&gt; Peter Sanchez&lt;br /&gt; Phil Rukin&lt;br /&gt; Philip K&lt;br /&gt; Philip Woelfel&lt;br /&gt; Philipe Goulet&lt;br /&gt; Philipp Ludwig&lt;br /&gt; Philipp Riegger&lt;br /&gt; Philippe Pepiot&lt;br /&gt; Philz69&lt;br /&gt; Pi-Yueh Chuang&lt;br /&gt; Pierre-Albéric TROUPLIN&lt;br /&gt; Piper McCorkle&lt;br /&gt; pixelherodev&lt;br /&gt; PlusMinus0&lt;br /&gt; PoroCYon&lt;br /&gt; ppascher&lt;br /&gt; Pranjal Kole&lt;br /&gt; ProgAndy&lt;br /&gt; progandy&lt;br /&gt; Przemyslaw Pawelczyk&lt;br /&gt; psykose&lt;br /&gt; punkkeks&lt;br /&gt; pyxel&lt;br /&gt; Quantum&lt;br /&gt; Quentin Carbonneaux&lt;br /&gt; Quentin Glidic&lt;br /&gt; Quentin Rameau&lt;br /&gt; R Chowdhury&lt;br /&gt; r-c-f&lt;br /&gt; Rabit&lt;br /&gt; Rachel K&lt;br /&gt; Rafael Castillo&lt;br /&gt; rage 311&lt;br /&gt; Ragnar Groot Koerkamp&lt;br /&gt; Ragnis Armus&lt;br /&gt; Rahiel Kasim&lt;br /&gt; Raman Varabets&lt;br /&gt; Ranieri Althoff&lt;br /&gt; Ray Ganardi&lt;br /&gt; Raymond E. Pasco&lt;br /&gt; René Wagner&lt;br /&gt; Reto Brunner&lt;br /&gt; Rex Hackbro&lt;br /&gt; Ricardo Wurmus&lt;br /&gt; Richard Bradfield&lt;br /&gt; Rick Cogley&lt;br /&gt; rinpatch&lt;br /&gt; Robert Günzler&lt;br /&gt; Robert Johnstone&lt;br /&gt; Robert Kubosz&lt;br /&gt; Robert Sacks&lt;br /&gt; Robert Vollmert&lt;br /&gt; Robin Jarry&lt;br /&gt; Robin Kanters&lt;br /&gt; Robin Krahl&lt;br /&gt; Robin Opletal&lt;br /&gt; Robinhuett&lt;br /&gt; robotanarchy&lt;br /&gt; Rodrigo Lourenço&lt;br /&gt; Rohan Kumar&lt;br /&gt; Roman Gilg&lt;br /&gt; ROMB&lt;br /&gt; Ronan Pigott&lt;br /&gt; ronys&lt;br /&gt; Roosembert Palacios&lt;br /&gt; roshal&lt;br /&gt; Roshless&lt;br /&gt; Ross L&lt;br /&gt; Ross Schulman&lt;br /&gt; Rostislav Pehlivanov&lt;br /&gt; rothair&lt;br /&gt; RoughB Tier0&lt;br /&gt; Rouven Czerwinski&lt;br /&gt; rpigott&lt;br /&gt; Rune Morling&lt;br /&gt; russ morris&lt;br /&gt; Ryan Chan&lt;br /&gt; Ryan Dwyer&lt;br /&gt; Ryan Farley&lt;br /&gt; Ryan Gonzalez&lt;br /&gt; Ryan Walklin&lt;br /&gt; Rys Sommefeldt&lt;br /&gt; Réouven Assouly&lt;br /&gt; S. Christoffer Eliesen&lt;br /&gt; s0r00t&lt;br /&gt; salkin-mada&lt;br /&gt; Sam Newbold&lt;br /&gt; Sam Whited&lt;br /&gt; SatowTakeshi&lt;br /&gt; Sauyon Lee&lt;br /&gt; Scoopta&lt;br /&gt; Scott Anderson&lt;br /&gt; Scott Colby&lt;br /&gt; Scott Leggett&lt;br /&gt; Scott Moreau&lt;br /&gt; Scott O’Malley&lt;br /&gt; Scott Stevenson&lt;br /&gt; sdilts&lt;br /&gt; Sebastian&lt;br /&gt; Sebastian Krzyszkowiak&lt;br /&gt; Sebastian LaVine&lt;br /&gt; Sebastian Noack&lt;br /&gt; Sebastian Parborg&lt;br /&gt; Seferan&lt;br /&gt; Sergeeeek&lt;br /&gt; Sergei Dolgov&lt;br /&gt; Sergi Granell&lt;br /&gt; sergio&lt;br /&gt; Seth Barberee&lt;br /&gt; Seán C McCord&lt;br /&gt; sghctoma&lt;br /&gt; Shaw Vrana&lt;br /&gt; Sheena Artrip&lt;br /&gt; Silvan Jegen&lt;br /&gt; Simon Barth&lt;br /&gt; Simon Branch&lt;br /&gt; Simon Ruderich&lt;br /&gt; Simon Ser&lt;br /&gt; Simon Zeni&lt;br /&gt; Siva Mahadevan&lt;br /&gt; skip-yell&lt;br /&gt; skuzzymiglet&lt;br /&gt; Skyler Riske&lt;br /&gt; Slowpython&lt;br /&gt; Sol Fisher Romanoff&lt;br /&gt; Solomon Victorino&lt;br /&gt; somdoron&lt;br /&gt; Sorcus&lt;br /&gt; sourque&lt;br /&gt; Spencer Michaels&lt;br /&gt; SpizzyCoder&lt;br /&gt; sqwishy&lt;br /&gt; Srivathsan Murali&lt;br /&gt; Stacy Harper&lt;br /&gt; Steef Hegeman&lt;br /&gt; Stefan Rakel&lt;br /&gt; Stefan Schick&lt;br /&gt; Stefan Tatschner&lt;br /&gt; Stefan VanBuren&lt;br /&gt; Stefan Wagner&lt;br /&gt; Stefano Ragni&lt;br /&gt; Stephan Hilb&lt;br /&gt; Stephane Chauveau&lt;br /&gt; Stephen Brennan&lt;br /&gt; Stephen Brown II&lt;br /&gt; Stephen Gregoratto&lt;br /&gt; Stephen Paul Weber&lt;br /&gt; Steve Jahl&lt;br /&gt; Steve Losh&lt;br /&gt; Steven Guikal&lt;br /&gt; Stian Furu Øverbye&lt;br /&gt; Streetwalrus Einstein&lt;br /&gt; Stuart Dilts&lt;br /&gt; Sudipto Mallick&lt;br /&gt; Sumner Evans&lt;br /&gt; Syed Amer Gilani&lt;br /&gt; sykhro&lt;br /&gt; Tadeo Kondrak&lt;br /&gt; Taiyu&lt;br /&gt; taiyu&lt;br /&gt; taminaru&lt;br /&gt; Tamir Zahavi-Brunner&lt;br /&gt; Tancredi Orlando&lt;br /&gt; Tanguy Fardet&lt;br /&gt; Tarmack&lt;br /&gt; Taryn Hill&lt;br /&gt; tastytea&lt;br /&gt; tcb&lt;br /&gt; Teddy Reed&lt;br /&gt; Tero Koskinen&lt;br /&gt; Tharre&lt;br /&gt; Thayne McCombs&lt;br /&gt; The Depressed Milkman&lt;br /&gt; TheAvidDev&lt;br /&gt; TheMachine02&lt;br /&gt; Theodor Thornhill&lt;br /&gt; thermitegod&lt;br /&gt; Thiago Mendes&lt;br /&gt; thirtythreeforty&lt;br /&gt; Thomas Bracht Laumann Jespersen&lt;br /&gt; Thomas Hebb&lt;br /&gt; Thomas Jespersen&lt;br /&gt; Thomas Karpiniec&lt;br /&gt; Thomas Merkel&lt;br /&gt; Thomas Plaçais&lt;br /&gt; Thomas Schneider&lt;br /&gt; Thomas Weißschuh&lt;br /&gt; Thomas Wouters&lt;br /&gt; Thorben Günther&lt;br /&gt; thuck&lt;br /&gt; Till Hofmann&lt;br /&gt; Tim Sampson&lt;br /&gt; Tim Schumacher&lt;br /&gt; Timidger&lt;br /&gt; Timmy Douglas&lt;br /&gt; Timothée Floure&lt;br /&gt; Ting-Wei Lan&lt;br /&gt; tiosgz&lt;br /&gt; toadicus&lt;br /&gt; Tobi Fuhrimann&lt;br /&gt; Tobias Blass&lt;br /&gt; Tobias Langendorf&lt;br /&gt; Tobias Stoeckmann&lt;br /&gt; Tobias Wölfel&lt;br /&gt; Tom Bereknyei&lt;br /&gt; Tom Lebreux&lt;br /&gt; Tom Ryder&lt;br /&gt; Tom Warnke&lt;br /&gt; tomKPZ&lt;br /&gt; Tommy Nguyen&lt;br /&gt; Tomáš Čech&lt;br /&gt; Tony Crisci&lt;br /&gt; Torstein Husebø&lt;br /&gt; Trannie Carter&lt;br /&gt; Trevor Slocum&lt;br /&gt; TriggerAu&lt;br /&gt; Tudor Brindus&lt;br /&gt; Tudor Roman&lt;br /&gt; Tuomas Siipola&lt;br /&gt; tuomas56&lt;br /&gt; Twan Wolthof&lt;br /&gt; Tyler Anderson&lt;br /&gt; Uli Schlachter&lt;br /&gt; Umar Getagazov&lt;br /&gt; unlimitedbacon&lt;br /&gt; unraised&lt;br /&gt; User Name&lt;br /&gt; v44r&lt;br /&gt; Valentin&lt;br /&gt; Valentin Hăloiu&lt;br /&gt; Vasilij Schneidermann&lt;br /&gt; Versus Void&lt;br /&gt; vexhack&lt;br /&gt; Vijfhoek&lt;br /&gt; vil&lt;br /&gt; vilhalmer&lt;br /&gt; Vincent Gu&lt;br /&gt; Vincent Vanlaer&lt;br /&gt; Vinko Kašljević&lt;br /&gt; Vitalij&lt;br /&gt; Vitalij Mikhailov&lt;br /&gt; Vlad Pănăzan&lt;br /&gt; Vlad-Stefan Harbuz&lt;br /&gt; Vyivel&lt;br /&gt; w1ke&lt;br /&gt; Wagner Riffel&lt;br /&gt; wagner riffel&lt;br /&gt; Wai Hon Law&lt;br /&gt; wb9688&lt;br /&gt; wdbw&lt;br /&gt; Whemoon Jang&lt;br /&gt; Wieland Hoffmann&lt;br /&gt; Wiktor Kwapisiewicz&lt;br /&gt; wil&lt;br /&gt; Will Daly&lt;br /&gt; Will Hunt&lt;br /&gt; willakat&lt;br /&gt; Willem Sonke&lt;br /&gt; William Casarin&lt;br /&gt; William Culhane&lt;br /&gt; William Durand&lt;br /&gt; William Moorehouse&lt;br /&gt; William Wold&lt;br /&gt; willrandship&lt;br /&gt; Willy Goiffon&lt;br /&gt; Wolf480pl&lt;br /&gt; Wouter van Kesteren&lt;br /&gt; Xaiier&lt;br /&gt; xdavidwu&lt;br /&gt; xPMo&lt;br /&gt; y0ast&lt;br /&gt; Yacine Hmito&lt;br /&gt; yankejustin&lt;br /&gt; Yasar&lt;br /&gt; Yash Srivastav&lt;br /&gt; Yong Joseph Bakos&lt;br /&gt; Yorick van Pelt&lt;br /&gt; yuiiio&lt;br /&gt; yuilib&lt;br /&gt; Yury Krivopalov&lt;br /&gt; Yuya Nishihara&lt;br /&gt; Yábir Benchakhtir&lt;br /&gt; Yábir García&lt;br /&gt; Zach DeCook&lt;br /&gt; Zach Sisco&lt;br /&gt; Zachary King&lt;br /&gt; Zandr Martin&lt;br /&gt; zccrs&lt;br /&gt; Zetok Zalbavar&lt;br /&gt; Zie&lt;br /&gt; Zoltan Kalmar&lt;br /&gt; Zuzana Svetlikova&lt;br /&gt; Éloi Rivard&lt;br /&gt; Érico Rolim&lt;br /&gt; Štěpán Němec&lt;br /&gt; наб&lt;br /&gt; حبيب الامين&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Each of these is a distinct person, with their own lives and aspirations, who took time out of those lives to help build some cool software. I owe everything to these wonderful, talented, dedicated people. Thank you, everyone. Let’s keep up the good work, together.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/It-takes-a-village/</link>
        
        <pubDate>Mon, 14 Mar 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/It-takes-a-village/</guid>
      </item>
    
      <item>
        
        
          <title>Why am I building a programming language in private?</title>
          <description>
            &lt;p&gt;As many readers are aware, I have been working on designing and implementing a systems programming language. This weekend, I’ve been writing a PNG file decoder in it, and over the past week, I have been working on a simple kernel with it as well. I’m very pleased with our progress so far — I recently remarked that this language feels like the language I always wanted, and that’s mission accomplished by any definition I care to consider.&lt;/p&gt;&lt;p&gt;I started the project on December 27th, 2019, just over two years ago, and I have kept it in a semi-private state since. Though I have not given its name in public, the git repos, mailing lists, and bug trackers use sourcehut’s “unlisted” state, so anyone who knows the URL can see them. The website is also public, though its domain name is also undisclosed, and it is full of documentation, tutorials, and resources for developers. People can find the language if they want to, though at this stage the community only welcomes contributors, not users or onlookers. News of the project nominally spreads by word of mouth and with calls-to-action on this blog, and to date a total of 30 people have worked on it over the course of 3,029 commits. It is a major, large-scale project, secret though it may be.&lt;/p&gt;&lt;p&gt;And, though we’ve invested a ton of work into this project together, it remains as-of-yet unfinished. There is no major software written in our language, though several efforts are underway. Several of our key goals have yet to be merged upstream, such as date/time support, TLS, and regular expressions, though, again, these efforts are well underway. Until we have major useful projects written in our language, we cannot be confident in our design, and efforts in these respects do a great deal to inform us regarding any changes which might be necessary. And some changes are already in the pipeline: we have plans to make several major revisions to the language and standard library design, which are certain to require changes in downstream software.&lt;/p&gt;&lt;p&gt;When our community is small and private, these changes are fairly easy to reckon with. Almost everyone who is developing a project based on our language is also someone who has worked on the compiler or standard library. Often, the person who implements a breaking change will also send patches to various downstreams updating them to be compatible with this change, &lt;em&gt;for every extant software project written in the language&lt;/em&gt;. This is a task which can be undertaken by one person. We all understand the need for these changes, participate in the discussions and review the implementations, and have the expertise necessary to make the appropriate changes to our projects.&lt;/p&gt;&lt;p&gt;Moreover, all of these people are also understanding of the in-development nature of the project. All users of our language are equipped with the knowledge that they are expected to help fix the bugs they identify, and with the skills and expertise necessary to follow-up on this fact. We don’t have to think about users who stumble upon the project, spend a few hours trying to use it, then encounter an under-developed part of the language and run out of enthusiasm. We still lack DWARF support, so debugging is a chore. Sometimes the compiler segfaults or aborts without printing a useful error message. It’s a work-in-progress, after all. These kinds of problems can discourage new learners very fast, and often requires the developers to offer some of their precious bandwidth to provide expert assistance. With the semi-private model, there are, at any given time, a very small number of people involved who are new to the language and require more hands-on support to help them through their problems.&lt;/p&gt;&lt;p&gt;A new programming language is a major undertaking. We’re building one with an explicit emphasis on simplicity and we’re still not done after two years. When most people hear about the project for the first time, I don’t want them to find a half-completed language which they will fail to apply to their problem because it’s not fleshed out for their use-case. The initial release will have comprehensive documentation, a detailed specification, and stability guarantees, so it can be picked up and used in production by curious users on day one. I want to fast-forward to the phase where people study it to learn how to apply it to their problems, rather than to learn &lt;em&gt;if they can&lt;/em&gt; apply it to their problems.&lt;/p&gt;&lt;p&gt;Even though it is under development in private, this project is both “free software” and “open source”, according to my strict understanding of those terms as defined by the FSF and OSI. “Open source” does not mean that the project has a public face. The compiler is GPL 3.0 licensed, the standard library is MPL 2.0, and the specification is CC-BY-ND (the latter is notably less free, albeit for good reasons), and these details are what matter. Every person who has worked on the project, and every person who stumbles upon it, possesses the right to lift the veil of secrecy and share it with the world. The reason they don’t is because I asked them not to, and we maintain a mutual understanding regarding the need for privacy.&lt;/p&gt;&lt;p&gt;On a few occasions, someone has discovered the project and taken it upon themselves to share it in public places, including Hacker News, Lemmy, and 4chan. While this is well within your rights, I ask you to respect our wishes and allow us to develop this project in peace. I know that many readers are excited to try it out, but please give us some time and space to ensure that you are presented with a robust product. At the moment, we anticipate going public early next year. Thank you for your patience.&lt;/p&gt;&lt;p&gt;Thank you for taking the time to read my thoughts as well. I welcome your thoughts and opinions on the subject: &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;my inbox is always open&lt;/a&gt;. If you disagree, I would appreciate it if you reached out to me to discuss it before posting about the project online. And, if you want to get involved, here is a list of things we could use help with — email me to volunteer if you have both the time and expertise necessary:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cryptography&lt;/li&gt;&lt;li&gt;Ports for new architectures or operating systems&lt;/li&gt;&lt;li&gt;Image &amp; pixel formats/conversions&lt;/li&gt;&lt;li&gt;SQL database adapters&lt;/li&gt;&lt;li&gt;Signal handling&lt;/li&gt;&lt;li&gt;JSON parsing &amp; encoding&lt;/li&gt;&lt;li&gt;Compression and decompression&lt;/li&gt;&lt;li&gt;Archive formats&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you definitely don’t want to wait for the language to go public, volunteering in one of our focus areas is the best way to get involved. Get in touch! If not, then the release will come around sooner than you think. We’re depending on your patience and trust.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;Update 2022-03-14&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This blog post immediately generated detailed discussions on Hacker News and Lobsters in which people posted the language’s website and started tearing into everything they don’t like about it.&lt;/p&gt;&lt;p&gt;It’s not done yet, and the current state of the language is not representative of the project goals. This post was not a marketing stunt. It was a heartfelt appeal to your better nature.&lt;/p&gt;&lt;p&gt;You know, I have a lot on my plate. All of it adds up to a lot of stress. I had hoped that you would help relieve some of that stress by taking me seriously when I explained my motivations and asked nicely for you to leave us be. I was wrong.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Why-am-I-working-in-private/</link>
        
        <pubDate>Sun, 13 Mar 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Why-am-I-working-in-private/</guid>
      </item>
    
      <item>
        
        
          <title>Open Source is defined by the OSI&apos;s Open Source Definition</title>
          <description>
            &lt;p&gt;The &lt;a href=&quot;https://opensource.org&quot; target=&quot;_blank&quot;&gt;Open Source Initiative&lt;/a&gt; (OSI) publishes a document called the &lt;a href=&quot;https://opensource.org/osd&quot; target=&quot;_blank&quot;&gt;Open Source Definition&lt;/a&gt; (OSD), which defines the term “open source”. However, there is a small minority of viewpoints within the software community which wishes that this were not so. The most concerning among them are those who wish open source was more commercially favorable to &lt;em&gt;themselves&lt;/em&gt;, and themselves alone, such as companies like Elastic.&lt;/p&gt;&lt;p&gt;I disagree with this perspective, and I’d like take a few minutes today to explore several of the most common arguments in favor of this view, and explain why I don’t agree with them. One of the most frustrating complications in this discussion is the context of &lt;a href=&quot;https://en.wikipedia.org/wiki/Motivated_reasoning&quot; target=&quot;_blank&quot;&gt;motivated reasoning&lt;/a&gt; (&lt;a href=&quot;https://xkcd.com/2167&quot; target=&quot;_blank&quot;&gt;relevant xkcd&lt;/a&gt;): most people arguing in favor of an unorthodox definition of “open source” have a vested interest in their alternative view.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This makes it difficult to presume good faith. For example, say someone wants to portray their software as open source even if it prohibits commercial use by third parties, which would normally disqualify it as such. Their interpretation serves to re-enforce their commercialization plans, providing a direct financial incentive not only for them to promote this definition of “open source”, but also for them to convince you that their interpretation is valid.&lt;/p&gt;&lt;p&gt;I find this argument to be fundamentally dishonest. Let me illustrate this with an analogy. Consider &lt;a href=&quot;https://www.postgresql.org&quot; target=&quot;_blank&quot;&gt;PostgreSQL&lt;/a&gt;. If I were to develop a new program called Postgres which was similar to PostgreSQL, but different in some important ways — let’s say it’s a proprietary, paid, hosted database service — that would be problematic. The industry understands that “Postgres” refers to the popular open source database engine, and by re-using their name I am diluting the brand of Postgres. It can be inferred that my reasoning for this comes from the desire to utilize their brand power for personal commercial gain. The terms “Postgres” and “PostgreSQL” are trademarked, but even if they were not, this approach would be dishonest and ethically wrong.&lt;/p&gt;&lt;p&gt;So too are the attempts to re-brand “open source” in a manner which is more commercially exploitable for an individual person or organization equally dishonest. The industry has an orthodox understanding of the meaning of “open source”, i.e. that defined by the Open Source Initiative, which is generally well-understood through the proliferation of software licenses which are compatible with the OSD. When a project describes itself as “open source”, this is a useful short-hand for understanding that the project adheres to a specific set of values and offers a specific set of rights to its users and contributors. When those rights are denied or limited, the OSD no longer applies and thus neither does the term “open source”. To disregard this in the interests of a financial incentive is dishonest, much like I would be dishonest for selling “cakes” and fulfilling orders with used car tires with “cake” written on them instead.&lt;/p&gt;&lt;p&gt;Critics of the OSD frequently point out that the OSI failed to register a trademark on the term “open source”, but a trademark is not necessary for this argument to hold. Language is defined by its usage, and the OSD is the popular usage of the term “open source”, without relying on the trademark system. The existence of a trademark on a specific term is not required for language which misuses that term to be dishonest.&lt;/p&gt;&lt;p&gt;As language is defined by its usage, some may argue that they are as entitled as anyone else to put forward an alternative usage. This is how language evolves. They are not wrong, though I might suggest that their alternative usage of “open source” requires a substantial leap in understanding which might not be as agreeable to those who don’t stand to benefit financially from that leap. Even so, I argue that the mainstream definition of open source, that forwarded by the OSI, is a &lt;em&gt;useful&lt;/em&gt; term that is worth preserving in its current form. It is useful to quickly understand the essential values and rights associated with a piece of software as easily as stating that it is “open source”. I am not prepared to accept a new definition which removes or reduces important rights in service of your private financial interests.&lt;/p&gt;&lt;p&gt;The mainstream usage of “open source” under the OSD is also, in my opinion, morally just. You may feel a special relationship with the projects you start and invest into, and a sense of ownership with them, but they are not rightfully yours once you receive outside contributions. The benefit of open source is in the ability for the community to contribute directly to its improvements — and once they do, the project is the sum of your efforts &lt;em&gt;and&lt;/em&gt; the efforts of the community. Thus, is it not right that the right to commercial exploitation of the software is shared with that community? In the absence of a CLA,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; contributors retain their copyright as well, and the software is legally jointly owned by the sum of its contributors. And beyond copyright, the success of the software is the sum of its code along with the community who learns about and deploys it, offers each other support, writes blog posts and books about it, sells consulting services for it, and together helps to popularize it. If you wish to access all of these benefits of the open source model, you must play by the open source rules.&lt;/p&gt;&lt;p&gt;It’s not surprising that this would become a matter of contention among certain groups within the industry. Open source is not just eating the world, but &lt;em&gt;has eaten&lt;/em&gt; the world. Almost all software developed today includes substantial open source components. The open source brand is very strong, and there are many interests who would like to leverage that brand without meeting its obligations. But the constraints of the open source definition are &lt;em&gt;important&lt;/em&gt;, played a critical role in the ascension of open source in the software market, and worth preserving into the future.&lt;/p&gt;&lt;p&gt;That’s not to say that there isn’t room for competing ideologies. If you feel that the open source model does not work for you, then that’s a valid opinion to hold. I only ask that you market your alternative model honestly by using a different name for it. Software for which the source code is available, but which does not meet the requirements of the open source definition, is rightfully called “source available”. If you want a sexier brand for it, make one! “Open core” is also popular, though not exactly the same. Your movement has as much right to success as the open source movement, but you need to earn that success independently of the open source movement. Perhaps someday your alternative model will supplant open source! I wish you the best of luck in this endeavour.&lt;/p&gt;&lt;p&gt;&lt;em&gt;A previous version of this blog post announced that I had submitted my candidacy for the OSI board. Due to unforseen circumstances, I will be postponing my candidacy until the next election. I apologise for the confusion.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Open-source-is-defined-by-the-OSD/</link>
        
        <pubDate>Tue, 01 Mar 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Open-source-is-defined-by-the-OSD/</guid>
      </item>
    
      <item>
        
        
          <title>Plaid is an evil nightmare product from Security Hell</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://plaid.com&quot; target=&quot;_blank&quot;&gt;Plaid&lt;/a&gt; is a business that has built a widget that can be embedded in any of their customer’s websites which allows their customers to configure integrations with a list of third-party service providers. To facilitate this, Plaid pops up a widget on their customer’s domain which asks the end-user to &lt;em&gt;type in their username and password&lt;/em&gt; for the third-party service provider. If necessary, they will ask for a 2FA code. This is done without the third party’s permission, presumably through a browser emulator and a provider-specific munging shim, and collects the user’s credentials on a domain which is operated by neither the third party nor by Plaid.&lt;/p&gt;&lt;p&gt;The third-party service provider in question is the end-user’s bank.&lt;/p&gt;&lt;p&gt;What the actual fuck!&lt;/p&gt;&lt;p&gt;Plaid has weighed on my mind for a while, though I might have just ignored them if they hadn’t been enjoying a sharp rise in adoption across the industry. For decades, we have stressed the importance of double-checking the domain name and the little TLS “lock” icon before entering your account details for anything. It is perhaps the single most important piece of advice the digital security community has tried to bring into the public conciousness. Plaid wants to throw out all of those years of hard work and ask users to enter their freaking &lt;em&gt;bank credentials&lt;/em&gt; into a third-party form.&lt;/p&gt;&lt;p&gt;The raison d’être for Plaid is that banks are infamously inflexible and slow on the uptake for new technology. The status quo which Plaid aims to disrupt (ugh), at least for US bank account holders, involves the user entering their routing number and account number into a form. The service provider makes two small (&lt;$1) deposits, and when they show up on the user’s account statement a couple of days later, the user confirms the amounts with the service provider, the service provider withdraws the amounts again, and the integration is complete. The purpose of this dance is to provide a sufficiently strong guarantee that the account holder is same person who is configuring the integration.&lt;/p&gt;&lt;p&gt;This process is annoying. Fixing it would require banks to develop, deploy, and standardize on better technology, and, well, good luck with that. And, honestly, a company which set out with the goal of addressing this problem ethically would have a laudable ambition. But even so, banks &lt;em&gt;are&lt;/em&gt; modernizing around the world, and tearing down the pillars of online security in exchange for a mild convenience is ridiculous.&lt;/p&gt;&lt;p&gt;A convincing argument can be made that this platform violates the Computer Fraud and Abuse Act. Last year, &lt;a href=&quot;https://www.jurist.org/news/2021/08/plaid-agrees-to-pay-58-million-in-data-privacy-class-action-lawsuit/&quot; target=&quot;_blank&quot;&gt;they paid out $58M&lt;/a&gt; in one of many lawsuits for scraping and selling your bank data. Plaid thus joins the ranks of Uber, AirBnB, and others like them in my reckoning as a “move fast and break laws” company. This platform can only exist if they are either willfully malignant or grossly incompetent. They’ve built something that they know is wrong, and are hoping that they can outrun the regulators.&lt;/p&gt;&lt;p&gt;This behavior is not acceptable. This company needs to be regulated into the dirt and made an example of. Shame on you Plaid, and shame on everyone involved in bringing this product to market. Shame on their B2B customers as well, who cannot, such as they may like to, offload ethical due-diligence onto their vendors. Please don’t work for these start-ups. &lt;a href=&quot;https://drewdevault.com/2020/05/05/We-are-complicit-in-our-employers-deeds.html&quot; target=&quot;_blank&quot;&gt;I hold employees complicit in their employer’s misbehavior&lt;/a&gt;. You have options, please go make the world a better place somewhere else.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Plaid-is-an-evil-nightmare-product/</link>
        
        <pubDate>Sat, 19 Feb 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Plaid-is-an-evil-nightmare-product/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, February 2022</title>
          <description>
            &lt;p&gt;Hello once again! Another month of free software development goes by with lots of progress in all respects.&lt;/p&gt;&lt;p&gt;I will open with some news about &lt;a href=&quot;https://godocs.io&quot; target=&quot;_blank&quot;&gt;godocs.io&lt;/a&gt;: version 1.0 of &lt;a href=&quot;https://sr.ht/~sircmpwn/godocs.io&quot; target=&quot;_blank&quot;&gt;our fork of gddo&lt;/a&gt; has been released! Big thanks to Adnan Maolood for his work on this. I’m very pleased that, following our fork, we were not only able to provide continuity for godoc.org, but also to simplify, refactor, and improve the underlying software considerably. Check out &lt;a href=&quot;https://adnano.co/2022/02/10/godocs.io-one-year-later/&quot; target=&quot;_blank&quot;&gt;Adnan’s blog post&lt;/a&gt; for more details.&lt;/p&gt;&lt;p&gt;In programming language news, we have had substantial progress in many respects. One interesting project I’ve started is a Redis protocol implementation:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;SET&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;EX&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 10&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 10&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another contributor has been working on expanding our graphics support, including developing a backend for &lt;a href=&quot;https://github.com/Dav1dde/glad&quot; target=&quot;_blank&quot;&gt;glad&lt;/a&gt; to generate OpenGL bindings, and a linear algebra library ala &lt;a href=&quot;https://glm.g-truc.net/&quot; target=&quot;_blank&quot;&gt;glm&lt;/a&gt; for stuff like vector and matrix manipulation. Other new modules include a &lt;a href=&quot;https://drewdevault.com/2022/01/28/Implementing-mime-in-xxxx.html&quot; target=&quot;_blank&quot;&gt;MIME database&lt;/a&gt; and encoding::base32. Cryptography progress continued with the introduction of XTS mode for AES, which is useful for full disk encryption implementations, but has slowed while we develop bigint support for future algorithms like RSA. I have also been rewriting the language introduction tutorial with a greater emphasis on practical usage.&lt;/p&gt;&lt;p&gt;Before we move on from the language project: I need your help! I am looking for someone to help develop terminal support. This is fairly straightforward, though laborsome: it involves developing libraries in our language which provide the equivalents of something like ncurses (or, better, &lt;a href=&quot;http://www.leonerd.org.uk/code/libtickit/&quot; target=&quot;_blank&quot;&gt;libtickit&lt;/a&gt;), as well as the other end like &lt;a href=&quot;http://www.leonerd.org.uk/code/libvterm/&quot; target=&quot;_blank&quot;&gt;libvterm&lt;/a&gt; offers. Please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email me&lt;/a&gt; if you want to help.&lt;/p&gt;&lt;p&gt;In SourceHut news, we have &lt;a href=&quot;https://sourcehut.org/blog/2022-02-02-welcome-conrad/&quot; target=&quot;_blank&quot;&gt;hired&lt;/a&gt; our third full-time engineer: Conrad Hoffmann! Check out the blog post for details. The first major effort from Adnan’s NLnet-sponsored SourceHut work also landed yesterday, introducing GraphQL-native webhooks to git.sr.ht alongside a slew of other improvements. pages.sr.ht also saw some improvements that allow users to configure their site’s behavior more closely. Check out the “What’s cooking” post later today for all of the SourceHut news.&lt;/p&gt;&lt;p&gt;That’s all for today, thanks for reading!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-February-2022/</link>
        
        <pubDate>Tue, 15 Feb 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-February-2022/</guid>
      </item>
    
      <item>
        
        
          <title>Framing accessibility in broader terms</title>
          <description>
            &lt;p&gt;Upon hearing the term “accessibility”, many developers call to mind the HTML &lt;abbr title=&quot;Accessible Rich Internet Applications&quot;&gt;ARIA&lt;/abbr&gt; attributes and little else. Those who have done some real accessibility work may think of the &lt;abbr title=&quot;Web Content Accessibility Guidelines&quot;&gt;WCAG&lt;/abbr&gt; guidelines. Some FOSS developers&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; may think of &lt;abbr title=&quot;Assistive Technology Service
Provider Interface&quot;&gt;AT-SPI&lt;/abbr&gt;. The typical user of these accessibility features is, in the minds of many naive developers, a blind person. Perhaps for those who have worked with WCAG, a slightly more sophisticated understanding of the audience for accessibility tools may include users with a greater variety of vision-related problems, motor impairments, or similar needs.&lt;/p&gt;&lt;p&gt;Many developers&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; frame accessibility in these terms, as a list of boxes to tick off, or specific industry tools which, when used, magically create an accessible product. This is not the case. In truth, a much broader understanding of accessibility is required to create genuinely accessible software, and because that understanding often raises uncomfortable questions about our basic design assumptions, the industry’s relationship with accessibility borders on willful ignorance.&lt;/p&gt;&lt;p&gt;The typical developer’s relationship with accessibility, if they have one at all, is mainly concerned with making web pages work with screen readers. Even considering this very narrow goal, most developers have an even narrower understanding of the problem, and end up doing a piss-poor job of it. In essence, the process of doing accessibility badly involves making a web page for a sighted user, then using ARIA tags to hide cosmetic elements, adding alt tags, and making other surface-level improvements for users of screen readers. If they’re serious, they may reach for the WCAG guidelines and do things like considering contrast, font choices, and animations as well, but all framed within the context of adding accessibility band-aids onto a UI designed for sighted use.&lt;/p&gt;&lt;p&gt;A key insight here is that concerns like font choice and contrast involve making changes which are apparent to “typical” users as well, but we’ll expand on that in a moment. Instead of designing for people like you and then patching it up until it’s semi-functional for people who are not like you, a wise developer places themselves into the shoes of the person they’re designing for and builds something which speaks their design language. For visually impaired users, this might mean laying out information in a more &lt;em&gt;logical&lt;/em&gt; sense than in a &lt;em&gt;spatial&lt;/em&gt; sense.&lt;/p&gt;&lt;p&gt;Importantly, accessibility also means understanding that there are many other kinds of users who have accessibility needs.&lt;/p&gt;&lt;p&gt;For instance, consider someone who cannot afford a computer as nice as the one your developers are using. When your Electron &lt;s&gt;crapware&lt;/s&gt; app eats up 8G of RAM, it may be fine on your 32G developer workstation, but not so much for someone who cannot afford anything other than a used $50 laptop from eBay. Waking up the user’s phone every 15 minutes to check in with your servers isn’t very nice for someone using a 5-year-old phone with a dying battery. Your huge JavaScript bundle, unoptimized images, and always-on network requirements are not accessible to users who are on low-bandwidth mobile connections or have a data cap — you’re essentially charging poorer users a tax to use your website.&lt;/p&gt;&lt;p&gt;Localization is another kind of accessibility, and it requires more effort than running your strings through gettext. Users in different locales speak not only different natural languages, but different design languages. Users of right-to-left languages like Arabic don’t just reverse their strings but also the entire layout of the page. Chinese and Japanese users are more familiar with denser UIs than the typical Western user. And subtitles and transcripts are important for Deaf users, but also useful for users who are consuming your content in a second language.&lt;/p&gt;&lt;p&gt;Intuitiveness is another important detail. Not everyone understands what your icons mean, for a start. They may not have the motor skill to hold their mouse over the button and read the tool-tip, either, and might not know that they can do that in the first place! Reliance on unfamiliar design language in general is a kind of inaccessible design. Remember the “save” icon? 💾 Flashing banner ads are also inaccessible for users with ADHD, and if we’re being honest, for everyone else, too. Software which is not responsive on many kinds of devices (touch, mouse and keyboard, different screen sizes, aspect ratios, orientations) is not accessible. Software which requires the latest and greatest technologies to use (such as a modern web browser) is also not accessible.&lt;/p&gt;&lt;p&gt;Adequate answers to these problems are often expensive and uncomfortable, so no one wants to think about them. Social-media-esque designs which are deliberately addictive are not accessible, and also not moral. The mountain of gross abstractions on which much software is built is cheap, but causes it to suck up all the user’s resources (RAM, CPU, battery, etc) on 10-year-old devices.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; And ads are inaccessible &lt;em&gt;by design&lt;/em&gt;, but good luck explaining that to your boss.&lt;/p&gt;&lt;p&gt;It is a fool’s errand to aim for perfect accessibility for all users, but we need to understand that our design choices are excluding people from using our tools. We need to design our software with accessibility in mind from the ground up, and with a broad understanding of accessibility that acknowledges that simple, intuitive software is the &lt;em&gt;foundation&lt;/em&gt; of accessibility which works for everyone, including you and me — and not retroactively adding half-assed tools to fundamentally unusable software. I want UI designers to be thinking in these terms, and less in terms of aesthetic properties, profitable designs, and dark patterns. Design with empathy first.&lt;/p&gt;&lt;p&gt;As someone who works exclusively in free software, I have to acknowledge the fact that free software is pretty pathetic when it comes to accessibility. In our case, this does not generally come from the perverse incentives that cause businesses to cut costs or even deliberately undermine accessibility for profit,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; but instead comes from laziness (or, more charitably, lack of free time and enthusiasm), and generally from free software’s struggles to build software for people who are not like its authors. I think that we can change this. We do not have the profit motive, and we can choose to take pride in making better software for everyone. Let’s do better.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Framing-accessibility-in-broader-terms/</link>
        
        <pubDate>Sun, 13 Feb 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Framing-accessibility-in-broader-terms/</guid>
      </item>
    
      <item>
        
        
          <title>Free software licenses explained: MIT</title>
          <description>
            &lt;p&gt;This is the first in a series of posts I intend to write explaining how various free and open source software licenses work, and what that means for you as a user or developer of that software. Today we’ll look at the MIT license, also sometimes referred to as the X11 or Expat license.&lt;/p&gt;&lt;p&gt;The MIT license is:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Both &lt;a href=&quot;https://www.gnu.org/philosophy/free-sw.en.html&quot; target=&quot;_blank&quot;&gt;free software&lt;/a&gt; and &lt;a href=&quot;https://opensource.org/osd&quot; target=&quot;_blank&quot;&gt;open source&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Permissive (and thus non-copyleft and non-viral)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This means that the license upholds the four essential freedoms of free software (the right to run, copy, distribute, study, change and improve the software) and all of the terms of the open source definition (largely the same). Further more, it is classified on the permissive/copyleft spectrum as a permissive license, meaning that it imposes relatively few obligations on the recipient of the license.&lt;/p&gt;&lt;p&gt;The full text of the license is quite short, so let’s read it together:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The MIT License (MIT)&lt;/p&gt;&lt;p&gt;Copyright (c) &lt;year&gt; &lt;copyright holders&gt;&lt;/p&gt;&lt;p&gt;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;/p&gt;&lt;p&gt;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&lt;/p&gt;&lt;p&gt;THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The first paragraph of the license enumerates the rights which you, as a recipient of the software, are entitled to. It’s this section which qualifies the license as free and open source software (assuming the later sections don’t disqualify it). The key grants are the right to “use” the software (freedom 0), to “modify” and “merge” it (freedom 1), and to “distribute” and “sell” copies (freedoms 2 and 3), “without restriction”. We also get some bonus grants, like the right to sublicense the software, so you could, for instance, incorporate it into a work which uses a less permissive license like the GPL.&lt;/p&gt;&lt;p&gt;All of this is subject to the conditions of paragraph two, of which there is only one: you must include the copyright notice and license text in any substantial copies or derivatives of the software. Thus, the MIT license requires &lt;em&gt;attribution&lt;/em&gt;. This can be achieved by simply including the full license text (copyright notice included) somewhere in your project. For a proprietary product, this is commonly hidden away in a menu somewhere. For a free software project, where the source code is distributed alongside the product, I often include it as a comment in the relevant files. You can also add your name or the name of your organization to the list of copyright holders when contributing to MIT-licensed projects, at least in the absence of a CLA.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The last paragraph sets the expectations for the recipient, and it is very important. This &lt;em&gt;disclaimer of warranty&lt;/em&gt; is ubiquitous in nearly all free and open source software licenses. The software is provided “as is”, which is to say, in whatever condition you found it in, for better or worse. There is no expectation of warranty (that is to say, any support you receive is from the goodwill of the authors and not from a contractual obligation), and there is no guarantee of “merchantability” (that you can successfully sell it), fitness for a particular purpose (that you can successfully use it to solve your problem), or noninfringement (such as with respect to relevant patents). That last detail may be of particular importance: the MIT license disclaims all liability for patents that you might infringe upon by using the software. Other licenses often address this case differently, such as Apache 2.0.&lt;/p&gt;&lt;p&gt;MIT is a good fit for projects which want to impose very few limitations on the use or reuse of the software by others. However, the permissibility of the license permits behaviors you might not like, such as creating a proprietary commercial fork of the software and selling it to others without supporting upstream. Note that the right to sell the software is an inalienable requirement of the free software and open source definitions, but other licenses can level the playing field a bit with strategies like copyleft and virality, on the other end of the permissibility spectrum. I’ll cover some relevant licenses in the future.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Free-software-licenses-MIT/</link>
        
        <pubDate>Mon, 07 Feb 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Free-software-licenses-MIT/</guid>
      </item>
    
      <item>
        
        
          <title>Implementing a MIME database in XXXX</title>
          <description>
            &lt;p&gt;&lt;em&gt;This is a (redacted) post from the internal blog of a new systems programming language we’re developing. The project is being kept under wraps until we’re done with it, so for this post I’ll be calling it &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt;. If you are interested in participating, &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;send me an email&lt;/a&gt; with some details about your background and I’ll get back to you.&lt;/em&gt;&lt;/p&gt;&lt;style&gt;
.redacted {
  color: black;
  background: black;
}
&lt;/style&gt;
&lt;p&gt;Recently, I have been working on implementing a parser for media types (commonly called MIME types) and a database which maps media types to file extensions and vice-versa. I thought this would be an interesting module to blog about, given that it’s only about 250 lines of code, does something useful and interesting, and demonstrates a few interesting &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; concepts.&lt;/p&gt;&lt;p&gt;The format for media types is more-or-less defined by &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2045&quot; target=&quot;_blank&quot;&gt;RFC 2045&lt;/a&gt;, specifically &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2045#section-5.1&quot; target=&quot;_blank&quot;&gt;section 5.1&lt;/a&gt;. The specification is not great. The grammar shown here is copied and pasted from parts of larger grammars in older RFCs, RFCs which are equally poorly defined. For example, the quoted-string nonterminal is never defined here, but instead comes from RFC 822, which defines it but also states that it can be “folded”, which technically makes the following a valid Media Type:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;text/plain;charset=&amp;quot;hello
 world&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or so I would presume, but the qtext terminal “cannot include CR”, which is the mechanism by which folding is performed in the first place, and… bleh. Let’s just implement a “reasonable subset” of the spec instead and side-step the whole folding issue.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This post will first cover parsing media types, then address our second goal: providing a database which maps media types to file extensions and vice versa.&lt;/p&gt;&lt;h2&gt;Parsing Media Types&lt;/h2&gt;&lt;p&gt;So, here’s what we’re going to implement today: we want to parse the following string:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;text/plain; charset=utf-8; foo=&amp;quot;bar baz&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The code for that I came up with for is as follows:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Parses a Media Type, returning a tuple of the content type (e.g.&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// &amp;quot;text/plain&amp;quot;) and a parameter parser object, or [[errors::invalid]] if the&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// input cannot be parsed.&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// To enumerate the Media Type parameter list, pass the type_params object into&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// [[next_param]]. If you do not need the parameter list, you can safely discard&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// the object. Note that any format errors following the &amp;quot;;&amp;quot; token will not&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// cause [[errors::invalid]] to be returned unless [[next_param]] is used to&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// enumerate all of the parameters.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;type_params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;cut&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mtype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;cut&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mtype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;typevalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;typevalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mtype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;tokenize&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function accepts a string as input, then returns a tagged union which contains either a tuple of &lt;code&gt;(str, type_params)&lt;/code&gt;, or a syntax error.&lt;/p&gt;&lt;p&gt;I designed this with particular attention to the memory management semantics. &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; uses manual memory management, and if possible it’s desirable to avoid allocating any additional memory so that the user of our APIs remains in control of the memory semantics. The return value is a sub-string borrowed from the “text/plain” part, as well as a tokenizer which is prepared to split the remainder of the string along the “;” tokens.&lt;/p&gt;&lt;p&gt;Inspiration for strings::cut comes from &lt;a href=&quot;https://github.com/golang/go/issues/46336&quot; target=&quot;_blank&quot;&gt;Go&lt;/a&gt;:&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-hare&quot; data-lang=&quot;hare&quot;&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;&lt;span class=&quot;redacted&quot;&gt;xxxx&lt;/span&gt;doc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cut&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Returns a string &quot;cut&quot; along the first instance of a delimiter, returning
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// everything up to the delimiter, and everything after the delimiter, in a
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// tuple.
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//      strings::cut(&quot;hello=world=foobar&quot;, &quot;=&quot;) // (&quot;hello&quot;, &quot;world=foobar&quot;)
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//      strings::cut(&quot;hello world&quot;, &quot;=&quot;)        // (&quot;hello world&quot;, &quot;&quot;)
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// The return value is borrowed from the &apos;in&apos; parameter.
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And strings::tokenize works like so:&lt;/p&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-hare&quot; data-lang=&quot;hare&quot;&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;&lt;span class=&quot;redacted&quot;&gt;xxxx&lt;/span&gt;doc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tokenize&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Returns a tokenizer which yields sub-strings tokenized by a delimiter.
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//      let tok = strings::tokenize(&quot;hello, my name is drew&quot;, &quot; &quot;);
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//      assert(strings::next_token(tok) == &quot;hello,&quot;);
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//      assert(strings::next_token(tok) == &quot;my&quot;);
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//      assert(strings::next_token(tok) == &quot;name&quot;);
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//      assert(strings::remaining_tokens(tok) == &quot;is drew&quot;);
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The RFC limits the acceptable characters for the media type and subtype, which we test with the typevalid function.&lt;/p&gt;&lt;p&gt;The user of this module often only cares about the media type and not its type parameters, so the tokenizer can be safely abandoned on the stack to get cleaned up when the stack frame exits if they don’t care about the rest.&lt;/p&gt;&lt;p&gt;This is enough to write a little test:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;attribute&quot;&gt;@test&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;image/png&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;image/png&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;application/svg+xml; charset=utf-8; foo=&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;bar baz&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;application/svg+xml&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;$ &lt;span class=&quot;redacted&quot;&gt;xxxx&lt;/span&gt; test mime::parse
mime::parse..................................... OK

1 passed; 0 failed; 1 tests completed in 0.10s&lt;/pre&gt;
&lt;p&gt;To handle the type parameters in the third case, we add this function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Returns the next parameter as a (key, value) tuple from a [[type_params]]&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// object that was prepared via [[parse]], void if there are no remaining&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// parameters, and [[errors::invalid]] if a syntax error was encountered.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;next_param&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;type_params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;next_token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;comment spell&quot;&gt;// empty parameter&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;type_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;cut&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;=&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// The RFC does not permit whitespace here, but whitespace is very&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// common in the wild. ¯\_(ツ)_/¯&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;hasprefix&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;quoted&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns a (key, value) tuple and advances to the next parameter, or returns void if there are no further parameters (or, if necessary, an error). This is pretty straightforward: the tokenizer prepared by parse is splitting the string on &lt;code&gt;;&lt;/code&gt; tokens, so we first fetch the next token. We then use strings::cut again to split it over the &lt;code&gt;=&lt;/code&gt; token, and after a quick trim to fix another RFC oversight, we can return it to the caller. Unless it’s using this pesky quoted-string terminal, which is where our implementation starts to show its weaknesses:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;quoted&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// We have only a basic implementation of quoted-string. It has a couple&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// of problems:&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;//&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// 1. The RFC does not define it very well&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// 2. The parts of the RFC which are ill-defined are rarely used&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// 3. Implementing quoted-pair would require allocating a new string&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;//&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// This implementation should handle most Media Types seen in practice&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// unless they&amp;apos;re doing something weird and ill-advised with them.&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;&amp;quot;&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think this implementation speaks for itself. It could be a bit faster if we didn’t do 3 × O(n) strings::contains calls, but someone will send a patch if they care. The completed test for this is:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;attribute&quot;&gt;@test&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;image/png&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;image/png&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;application/svg+xml; charset=utf-8; foo=&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;bar baz&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;application/svg+xml&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;next_param&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;charset&amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;next_param&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;foo&amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;bar baz&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;next_param&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;hi&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;text/ spaces &amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;text/@&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain;charset&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;next_param&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Media Type database&lt;/h2&gt;&lt;p&gt;The second part of this module is the Media Type database. This comes in two parts:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;An internal database which is populated by &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; modules. For example, an image::png module might register the “image/png” mimetype with the internal MIME database, similar to protocol registration for net::dial.&lt;/li&gt;&lt;li&gt;A system-provided database, usually via /etc/mime.types, which is more comprehensive, but may not be available at runtime.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I plan on doing the second part later on, so for now we’ll just focus on the first; most of the interesting bits are there anyway.&lt;/p&gt;&lt;p&gt;Again, special consideration is given to memory management here. The essence of a good &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; program or API design can be ascertained from how well it handles memory management. As such, I have set aside separate lists to handle statically allocated MIME info (such as those provided by image::png et al) versus the forthcoming dynamically-allocated system database.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// A pair of a Media Type and a list of file extensions associated with it. The&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// extension list does not include the leading &amp;apos;.&amp;apos; character.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;mime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// List of media types with statically allocated fields (though the list itself&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// is dynamically allocated).&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;static_db&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// List of media types with heap-allocated fields, used when loading mime types&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// from the system database.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;heap_db&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;builtins&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/x-xxxx&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// redacted for public blog post&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;xx&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@init&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;builtins&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@fini&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;fini&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;heap_db&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;heap_db&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;freeall&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;heap_db&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;heap_db&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;static_db&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The register function will be used from @init functions like this one to register media types with the internal database. This code has minimal allocations for the internal database, but we do actually do some allocating here to store the “static_db” slice. In theory we could eliminate this by statically provisioning a small number of slots to store the internal database in, but for this use-case the trade-off makes sense. There are use-cases where the trade-off does not make as much sense, however. For example, here’s how the command line arguments are stored for your program in the “os” module:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// The command line arguments provided to the program. By convention, the first&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// member is usually the name of the program.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Statically allocate arg strings if there are few enough arguments, saves a&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// syscall if we don&amp;apos;t need it.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;args_static&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@init&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;init_environ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;argc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;args_static&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;args_static&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fromc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fromc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@fini&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;fini_environ&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;argc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;args_static&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A similar approach is also used on yyp’s RISC-V kernel for &lt;a href=&quot;https://paste.sr.ht/~sircmpwn/acaa1e61e6bcb3e22e8b4bce7f233dcd844565eb&quot; target=&quot;_blank&quot;&gt;storing serial devices&lt;/a&gt; without any runtime memory allocations.&lt;/p&gt;&lt;p&gt;The internal database is likely to be small, but the system database is likely to have a lot of media types and file extensions registered, so it makes sense to build out an efficient means of accessing them. For this purpose I have implemented a simple hash map. &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; does not have a built-in map construct, nor generics. The design constraints of &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; are closer to C than to anything else, and as such, the trade-offs for first-class maps are similar to C, which is to say that they don’t make sense with our design. However, this use-case does not call for much sophistication, so a simple map will suffice.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;fnv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;MIME_BUCKETS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Hash tables for efficient database lookup by mimetype or extension&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mimetable&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;MIME_BUCKETS&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;exttable&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;MIME_BUCKETS&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Registers a Media Type and its extensions in the internal MIME database. This&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// function is designed to be used by @init functions for modules which&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// implement new Media Types.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;mime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;static_db&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;static_db&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mime&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;static_db&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;static_db&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fnv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mimetable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mimetable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fnv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;exttable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;exttable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A fixed-length array of slices is a common approach to hash tables in &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt;. It’s not a great design for hash tables whose size is not reasonably predictable in advance or which need to be frequently resized and rehashed, but it is pretty easy to implement and provides sufficient performance for use-cases like this. A re-sizable hash table, or tables using an alternate hash function, or the use of linked lists instead of slices, and so on — all of this is possible if the use-case calls for it, but must be written by hand.&lt;/p&gt;&lt;p&gt;Finally, we implement the look-up functions, which are very simple:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Looks up a Media Type based on the mime type string, returning null if&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// unknown.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;lookup_mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;mime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fnv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mimetable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mimetable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Looks up a Media Type based on a file extension, with or without the leading&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// &amp;apos;.&amp;apos; character, returning null if unknown.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;lookup_ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;ltrim&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;.&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fnv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;exttable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;exttable&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the sake of completeness, here are the tests:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;attribute&quot;&gt;@test&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;lookup_mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lookup_mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;foo/bar&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lookup_mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lookup_mime&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;text/x-xxxx&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/x-xxxx&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;xx&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@test&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;lookup_ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lookup_ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lookup_ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;.foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lookup_ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lookup_ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;.txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lookup_ext&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;xx&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;mimetype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;mime&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;text/x-xxxx&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;exts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;xx&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you have it! I will later implement some code which parses /etc/mime.types in @init and fills up the heap_db slice, and this lookup code should work with it without any additional changes.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Implementing-mime-in-xxxx/</link>
        
        <pubDate>Fri, 28 Jan 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Implementing-mime-in-xxxx/</guid>
      </item>
    
      <item>
        
        
          <title>Pine64 should re-evaluate their community priorities</title>
          <description>
            &lt;p&gt;Pine64 has a really interesting idea: make cheap hardware with low margins, get it into the hands of the FOSS community, and let them come up with the software. No one has ever done this before, at least not on this scale, and it’s a really neat idea! Pine64 is doing a lot to support the FOSS community bringing up its hardware, but I’m afraid that I have to ask them to do a bit more.&lt;/p&gt;&lt;p&gt;There’s a handful of different roles that need to be filled in on the software side of things to get this ecosystem going. Ordered from most to least important, these are (broadly speaking) as follows:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Implementing and upstreaming kernel drivers, u-Boot support, etc&lt;/li&gt;&lt;li&gt;Building out a robust telephony stack for Linux&lt;/li&gt;&lt;li&gt;Building a mobile user interface for Linux&lt;/li&gt;&lt;li&gt;Maintaining distros that tie it all together&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Again, this is ordered from most to least important, but in practice, the ecosystem prioritizes them in reverse. Pine64 themselves contribute no labor to any of these focus areas, and though they provide some funding, they provide it from the bottom of this list up, putting most of it into distros and very little into the kernel, bootloaders, or telephony. This is nice, but… why fund the distros at all? Distros are not the ones getting results in these focus areas. Their job is to &lt;em&gt;distribute&lt;/em&gt; the results of community efforts.&lt;/p&gt;&lt;p&gt;Don’t get me wrong, the distros do an important job and they ought to get the funding they need, but this is just creating fragmentation in the ecosystem. As one example, we could be installing the Linux distribution of our choice on the Pinebook Pro using a standard aarch64 UEFI ISO installer, just like we do for any other laptop, if someone spent a couple of weeks upstreaming the last 6 patches to mainline Linux and put together a suitable u-Boot payload to flash on the SPI flash chip. But, instead of one working solution for everyone, we have 20+ Linux distros publishing Pine64-specific images to flash to microSD cards.&lt;/p&gt;&lt;p&gt;The favorites, which is apparently Manjaro,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; compete for funding and then spend it each according to their own discretion working on the same problems. If we instead spent it on the focus areas directly, then Manjaro and all of the other distros would benefit from this work for free. The telephony stack is equally important, and equally sharable between distros, but isn’t really getting any dedicated funding. You can’t have a phone without telephony. The mobile UI is also important, but it’s the easiest part to build, and a working phone with a shitty UI is better than a phone with a pretty UI that doesn’t work.&lt;/p&gt;&lt;p&gt;The work is getting done, to be fair, but it’s getting done very slowly. Many of the distros targetting Linux for mobile devices have people working on the important focus areas, but as a matter of necessity: to accomplish their goals when no one else is working on these problems, they had to become experts and divide their limited volunteer time between distro maintenance and software development. As a result, they’ve become experts with specific allegiances and incentives, and though there’s some patch sharing and collaboration between distros, it’s done informally across a dozen independent organizational structures with varying degrees of collaboration based on a model which was stapled onto an inherently backwards system of priorities. In a system with limited resources (funding, developer time, etc), these inefficiencies can be very wasteful.&lt;/p&gt;&lt;p&gt;After I got my hands on the PineNote hardware, I quickly understood that it was likely going to suffer even moreso from this problem. A course change is called for if Pine64 wants to maximize their odds of success with their current and future product lines. I think that the best strategic decision would be to hire just one full-time software developer to specifically focus on development and upstreaming in Linux mainline, u-Boot mainline, ModemManager, etc, and on writing docs, collaborating with other projects, and so on. This person should be figuring out how to get generalized software solutions to unlock the potential of the hardware, focusing on getting it to the right upstreams, and distributing these solutions to the whole ecosystem.&lt;/p&gt;&lt;p&gt;It’s &lt;em&gt;awesome&lt;/em&gt; that Pine64 is willing to financially support the FOSS community around their devices, and as the ones actually selling the devices, they’re the only entity in this equation with the budget to actually do so. Pine64 is doing some really amazing work! However, a better financial strategy is called for here. Give it some thought, guys.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Pine64s-weird-priorities/</link>
        
        <pubDate>Tue, 18 Jan 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Pine64s-weird-priorities/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, January 2022</title>
          <description>
            &lt;p&gt;Happy New Year! I had a lovely time in Amsterdam. No one had prepared me for the (apparently infamous) fireworks culture of the Netherlands. I thought it was really cool.&lt;/p&gt;&lt;p&gt;Our programming language continues to improve apace. Our cryptography suite now includes Argon2, Salsa20/XSalsa20, ChaCha20/XChaCha20, and Poly1305, and based on these functions we have added libsodium-style high-level cryptographic utilities for AEAD and key derivation, with stream encryption, message signing and verification, and key exchange coming soon. We have also laid out the priorities for future crypto work towards supporting TLS, and on the way we expect to have ed25519/x25519 and Diffie-Hellman added soon. Perhaps enough to implement an SSH client?&lt;/p&gt;&lt;p&gt;I also implemented an efficient path manipulation module for the standard library (something I would really have liked to have in C!), and progress continues on date/time support. We also have a new MIME module (just for Media Types, not all of MIME) and I expect a patch implementing net::uri to arrive in my inbox soon. I also finished up cmsg support (for sendmsg and recvmsg), which is necessary for the Wayland implementation I’m working on (and was a major pain in the ass). I spent some time working with another collaborator, who is developing a RISC-V kernel in our language, implementing a serial driver for the SiFive UART, plus improving the device tree loader and UEFI support.&lt;/p&gt;&lt;p&gt;One of the standard library contributors also wrote a side-project to implement &lt;a href=&quot;https://raytracing.github.io/&quot; target=&quot;_blank&quot;&gt;Ray Tracing in One Weekend&lt;/a&gt; in our language:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://git.sr.ht/~turminal/raytracing/blob/master/example.png&quot;&gt;
&lt;figcaption&gt;A ray-traced image of many small, colorful balls with three large spheres, two of which have a mirrored surface that reflects the other balls.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;In other words, language development has been very busy in the past few weeks. Another note: I have prepared &lt;a href=&quot;https://fosdem.org/2022/schedule/event/lg_qbe/&quot; target=&quot;_blank&quot;&gt;a lightning talk&lt;/a&gt; for FOSDEM which talks about the backend that we’re using: &lt;a href=&quot;https://c9x.me/compile&quot; target=&quot;_blank&quot;&gt;qbe&lt;/a&gt;. Check it out!&lt;/p&gt;&lt;p&gt;In SourceHut news, we have brought on a new full-time contributor, Adnan Maolood, thanks to &lt;a href=&quot;https://sourcehut.org/blog/2022-01-10-nlnet-graphql-funding/&quot; target=&quot;_blank&quot;&gt;a generous grant from NLNet&lt;/a&gt;. We also have another full-time software engineer starting on February 1st (on our own dime), so I’m very much looking forward to that. Adnan will be helping us with the GraphQL work, and the new engineer will be working similarly to Simon and I on FOSS projects generally (and, hopefully, with GraphQL et al as well). Speaking of GraphQL, I’m putting the finishing touches on the todo.sr.ht writable API this week: legacy webhooks. These are nearly done, and following this we need to do the security review and acceptance testing, then we can ship. Adnan has been hard at work on adding GraphQL-native webhooks to git.sr.ht, which should also ship pretty soon.&lt;/p&gt;&lt;p&gt;That’s all for today. Thanks for reading! I’ll see you again in another month.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-January-2022/</link>
        
        <pubDate>Mon, 17 Jan 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-January-2022/</guid>
      </item>
    
      <item>
        
        
          <title>The RISC-V experience</title>
          <description>
            &lt;p&gt;I’m writing to you from a Sway session on Alpine Linux, which is to say from a setup quite similar to the one I usually write blog posts on, save for one important factor: a RISC-V CPU.&lt;/p&gt;&lt;p&gt;I’ll state upfront that what I’m using is not a very practical system. What I’m going to describe is all of the impractical hacks and workarounds I have used to build a “useful” RISC-V system on which I can mostly conduct my usual work. It has been an interesting exercise, and it bodes well for the future of RISC-V, but for all practical purposes the promise of RISC-V still lives in tomorrow, not today.&lt;/p&gt;&lt;p&gt;In &lt;a href=&quot;https://drewdevault.com/2018/12/20/Porting-Alpine-Linux-to-RISC-V.html&quot; target=&quot;_blank&quot;&gt;December of 2018&lt;/a&gt;, I wrote an article about the process of bootstrapping Alpine Linux for RISC-V on the HiFive Unleashed board. This board was essentially a crappy SoC built around a RISC-V CPU: a microSD slot, GPIO pins, an ethernet port, a little bit of RAM, and the CPU itself, in a custom form-factor.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Today I’m writing this on the HiFive Unmatched, which is a big step up: it’s a Mini-ITX form factor (that is, it fits in a standardized PC case) with 16G of RAM, and the ethernet, microSD, and GPIO ports are complemented with a very useful set of additional I/O via two M.2 slots, a PCIe slot, and a USB 3 controller, plus an SPI flash chip. I have an NVMe drive with my root filesystem on it and an AMD Radeon Pro WX 2100 GPU installed. In form, it essentially functions like a standard PC workstation.&lt;/p&gt;&lt;p&gt;I have been gradually working on bringing this system up to the standards that I expect from a useful PC, namely that it can run upstream Alpine Linux with minimal fuss. This was not really possible on the previous SiFive hardware, but I have got pretty close on this machine. I had to go to some lengths to get u-Boot to provide a working UEFI environment,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and I had to patch grub as well, but the result is that I can write a standard Alpine ISO to a USB stick, then boot it and install Alpine onto an NVMe normally, which then boots itself with UEFI with no further fiddling. I interact with it through three means: the on-board UART via a micro-USB cable (necessary to interact with u-Boot, grub, or the early Linux environment), or ethernet (once sshd is up), or with keyboard, mouse, and displays connected to the GPU.&lt;/p&gt;&lt;p&gt;Another of the standards I expect is that everything runs with upstream free software, perhaps with a few patches, but not from a downstream or proprietary tree. I’m pleased to report that I am running an unpatched mainline Linux 5.15.13 build. I am running mainline u-Boot with one patch to correct the name of a device tree node to match a change in Linux upstream. I have a patched grub build, but the relevant patches have been proposed for grub upstream. I have a spattering of patches applied to a small handful of userspace programs and libraries, but all of them only call for one or two patches applied to the upstream trees. Overall, this is quite good for something this bleeding edge — my Pinephone build is worse.&lt;/p&gt;&lt;p&gt;I have enclosed the system in a mini-ITX case and set it down on top of my usual x86_64 workstation, then moved a few of my peripherals and displays over to it to use it as my workstation for the day.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; I was able to successfully set up almost all of my standard workstation loadout on it, with some notable exceptions. Firefox is the most painful omission — bootstrapping Rust is an utter nightmare&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; and no one has managed to do it for Alpine Linux riscv64 yet (despite many attempts and lots of hours wasted), so anything which depends on it does not work. librsvg is problematic for the same reason; I had to patch a number of things to be configured without it. For web browsing I am using &lt;a href=&quot;https://sr.ht/~sircmpwn/visurf&quot; target=&quot;_blank&quot;&gt;visurf&lt;/a&gt;, which is based on Netsurf, and which works for many of the lightweight websites that I generally prefer to use, but not for most others.  For instance, I was unable to address an issue that was raised on GitLab today because I cannot render GitLab properly on this browser. SourceHut mostly works, of course, but it’s not exactly pleasant — I still haven’t found time to improve the SourceHut UI for NetSurf.&lt;/p&gt;&lt;a href=&quot;https://redacted.moe/f/6ad3d811.jpg&quot;&gt;
  &lt;img src=&quot;https://redacted.moe/f/6ad3d811.jpg&quot; alt=&quot;A picture of two computers stacked on on top of the other.&quot; style=&quot;max-width: 70%&quot;&gt;
&lt;/a&gt;

&lt;div class=&quot;text-center&quot;&gt;&lt;small&gt;The lower computer is my typical x86_64
workstation, and the upper computer is the RISC-V machine. The USB ports on the
side are not connected to the board, so I pulled a USB extension cord around
from the back. This is mainly useful for rapid iteration when working on a
RISC-V kernel that a colleague has been developing using our new programming
language. I can probably get netboot working later, but this works for
now.&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;Complicating things is the fact that my ordinary workstation uses two 4K displays. For example, my terminal emulator of choice is &lt;a href=&quot;https://codeberg.org/dnkl/foot&quot; target=&quot;_blank&quot;&gt;foot&lt;/a&gt;, but it uses CPU rendering and the 4K window is noticeably sluggish. Alacritty, which renders on the GPU, would probably fare better — but Rust spoils this again. I settled for &lt;a href=&quot;https://st.suckless.org/&quot; target=&quot;_blank&quot;&gt;st&lt;/a&gt;, which has acceptable performance (perhaps in no small part thanks to being upscaled from 1080p on this setup). visurf also renders on the CPU and is annoyingly slow; as a workaround I have taken to resizing the window to be much smaller while actively navigating and then scaling it back up to full size to read the final page.&lt;/p&gt;&lt;p&gt;CPU-bound programs can be a struggle. However, this system has a consumer workstation GPU plugged into its PCIe slot. Any time I can get the GPU to pick up the slack, it works surprisingly effectively. For example, I watched Dune (2021) today in 4K on this machine — buttery smooth, stunningly beautiful 4K playback — a feat that my Pinebook Pro couldn’t dream of. The GPU has a hardware HEVC decoder, and mpv and Sway can use dmabufs such that the GPU decodes and displays each frame without it ever having to touch the CPU, and meanwhile the NVMe is fast enough to feed it data at a suitable bandwidth. A carefully configured obs-studio is also able to record my 4K display at 30 FPS and encode it on the GPU with VAAPI with no lag, something that I can’t even do on-CPU on x86_64 very reliably. The board does not provide onboard audio, but being an audiophile I have a USB DAC available that works just fine.&lt;/p&gt;&lt;p&gt;I was able to play Armagetron Advanced at 120+ FPS in 4K, but that’s not exactly a demanding game. I also played SuperTuxKart, a more demanding game, at 1080p with all of the settings maxed out at a stable 30 FPS. I cannot test any commercial games, since I’m reasonably certain that there are no proprietary games that distribute a riscv64 build for Linux. If Ethan Lee is reading this, please get in touch so that we can work together on testing out a Celeste build.&lt;/p&gt;&lt;p&gt;My ordinary workday needs are mostly met on this system. For communication, my mail setup with aerc and postfix works just fine, and my normal Weechat setup works great for IRC.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; Much like any other day, I reviewed a few patches and spent some time working on a shell I’ve been writing in our new programming language. The new language is quite performant, so no issues there. I think if I had to work on SourceHut today, it might be less pleasant to work with Python and Go, or to work on the web UI without a performant web browser. Naturally, browsing Geminispace with gmnlm works great.&lt;/p&gt;&lt;p&gt;So, where does this leave us? I have unusually conservative demands of my computers. Even on high-end, top-of-the-line systems, I run a very lightweight environment, and that’s the way I like it. Even so, my modest demands stress the limits of this machine. If I relied more on a web browser, or on more GUI applications, or used a heavier desktop environment, or heavier programming environments, I would not be able to be productive on this system. Tomorrow, I expect to return to my x86_64 machine as my daily workstation and continue to use this machine as I have before, for RISC-V development and testing over serial and SSH. There are few use-cases for which this hardware, given its limitations, is adequate.&lt;/p&gt;&lt;p&gt;Even so, this is a very interesting system. The ability to incorporate more powerful components like DDR4 RAM, PCIe GPUs, NVMe storage, and so on, can make up for the slow CPU in many applications. Though many use-cases for this system must be explained under strict caveats, one use-case it certainly offers is a remarkably useful system with which to advance the development of the RISC-V FOSS ecosystem. I’m using it to work on Alpine Linux, on kernel hacking projects, compiler development, and more, on a CPU that is designed in adherence to an open ISA standard and runs on open source firmware. This is a fascinating product that promises great things for the future of RISC-V as a platform.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-RISC-V-experience/</link>
        
        <pubDate>Sat, 15 Jan 2022 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-RISC-V-experience/</guid>
      </item>
    
      <item>
        
        
          <title>Breaking down a small language design proposal</title>
          <description>
            &lt;style&gt;
.redacted { background: black; color: black; }
&lt;/style&gt;
&lt;p&gt;We are developing a new systems programming language. The name is a secret, so we’ll call it &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; instead. In &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; must be initialized. This is fine for the simple case, such as “let x: int = 10”. But, it does not always work well. Let’s say that you want to set aside a large buffer for I/O:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error type number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can clearly get out of hand. To address this problem, we added the “…” operator:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example demonstrates both &lt;em&gt;stack&lt;/em&gt; allocation of a buffer and &lt;em&gt;heap&lt;/em&gt; allocation of a buffer initialized with 1024 zeroes.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This “…” operator neatly solves our problem. However, another problem occurs to me: what if you want to allocate a buffer of a variable size?&lt;/p&gt;&lt;p&gt;In addition to arrays, &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; supports slices, which stores a data pointer, a length, and a capacity. The data pointer refers to an array whose length is equal to or greater than “capacity”, and whose values are initialized up to “length”. We have additional built-ins, “append”, “insert”, and “delete”, which can dynamically grow and shrink a slice.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 4&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 5&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// x = [1, 2, 3, 4, 5]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// x = [3, 4, 5]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;comment spell&quot;&gt;// x = [1, 2, 3, 4, 5]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also allocate a slice whose capacity is set to an arbitrary value, but whose length is only equal to the number of initializers you provide. This is done through a separate case in the “alloc” grammar:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;capacity&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 10&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful if you know how long the slice will eventually be, so that you can fill it with “append” without re-allocating (which could be costly otherwise). However, setting the capacity is not the same thing as setting the length: all of the items between the length and capacity are uninitialized. How do we zero-initialize a large buffer in the heap?&lt;/p&gt;&lt;p&gt;Until recently, you simply couldn’t. You had to use a rather bad work-around:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// [*] is an array of undefined length&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is obviously not great. We lose type safety, the initialization guarantee, and bounds checking, and we add a footgun (multiplying by the member type size), and it’s simply not very pleasant to use. To address this, we added the following syntax:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much better! Arriving at this required untangling a lot of other problems that I haven’t mentioned here, but this isn’t the design I want to focus on for this post. Instead, there’s a new question this suggests: what about appending a variable amount of data to a slice? I want to dig into this problem to explore some of the concerns we think about when working on the language design.&lt;/p&gt;&lt;p&gt;The first idea I came up with was the following:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 10&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would append ten zeroes to “x”. This has a problem, though. Consider our earlier example of “append”:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The grammar for this looks like the following:&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/d37abdd8.png&quot;&gt;
&lt;figcaption&gt;A screenshot of the language spec showing the grammar for append expressions.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;So, the proposed “append(x, [0…], 10)” expression is &lt;em&gt;parsed&lt;/em&gt; like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;slice-mutation-expression: append
    object-selector: x
    append-items:
        [0...]
        10
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In other words, it looks like “append the values [0…] and 10 to x”. This doesn’t make sense, but we don’t know this until we get to the type checker. What it really means is “append ten zeroes to x”, and we have to identify this case in the type checker through, essentially, heuristics. Not great! If we dig deeper into this we find even more edge cases, but I will spare you from the details.&lt;/p&gt;&lt;p&gt;So, let’s consider an alternative design:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// Previously append(x, 1, 2, 3);&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 10&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;comment spell&quot;&gt;// New feature&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The grammar for this is much better:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/b0e56d79.png&quot;&gt;
&lt;figcaption&gt;A screenshot of the revised grammar for this design.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Now we can distinguish between these cases while parsing, so the first example is parsed as:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;append-expression
    object-selector: x
    expression: [1, 2, 3]   // Items to append
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The second is parsed as:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;append-expression
    object-selector: x
    expression: [0...]    // Items to append
    expression: 10        // Desired length
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a big improvement, but it comes with one annoying problem. The most common case for append in regular use in &lt;span class=&quot;redacted &quot;&gt;xxxx&lt;/span&gt; is appending a single item, and this case has worsened thanks to this change:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;comment spell&quot;&gt;// Previously append(x, 42);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In fact, appending several items at once is exceptionally uncommon: there are no examples of it in the standard library. We should try to avoid making the common case worse for the benefit of the uncommon case.&lt;/p&gt;&lt;p&gt;A pattern we &lt;em&gt;do&lt;/em&gt; see in the standard library is appending one slice to another, which is a use-case we’ve ignored up to this point. This use-case looks something like the following:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why don’t we lean into this a bit more?&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 42&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// x = [42]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// x = [42, 1, 2, 3]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 6&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;       &lt;span class=&quot;comment spell&quot;&gt;// x = [42, 1, 2, 3, 0...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the &lt;code&gt;append(x, y...)&lt;/code&gt; syntax to generally handle appending several items neatly solves all of our problems. We have arrived at design which:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Is versatile and utilitarian&lt;/li&gt;&lt;li&gt;Addresses the most common cases with a comfortable syntax&lt;/li&gt;&lt;li&gt;Is unambiguous at parse time without type heuristics&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I daresay that, in addition to fulfilling the desired new feature, we have improved the other cases as well. The final grammar for this is the following:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/1d400f58.png&quot;&gt;
&lt;figcaption&gt;Formal grammar showing the final state of the design proposal&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;If you’re curious to see more, I’ve extracted the relevant page of the specification for you to read: &lt;a href=&quot;https://redacted.moe/f/9c48d5d4.pdf&quot; target=&quot;_blank&quot;&gt;download it here&lt;/a&gt;. I hope you found that interesting and insightful!&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: Much of these details are subject to change, and we have future improvements planned which will affect these features — particularly with respect to handling allocation failures. Additionally, some of the code samples were simplified for illustrative purposes.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Language-design-considerations/</link>
        
        <pubDate>Thu, 30 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Language-design-considerations/</guid>
      </item>
    
      <item>
        
        
          <title>Please don&apos;t use Discord for FOSS projects</title>
          <description>
            &lt;p&gt;Six years ago, I wrote a post speaking out against the use of Slack for the instant messaging needs of FOSS projects. In retrospect, this article is not very good, and in the years since, another proprietary chat fad has stepped up to bat: Discord. It’s time to revisit this discussion.&lt;/p&gt;&lt;p&gt;In short, using Discord for your free software/open source (FOSS) software project is a very bad idea. Free software matters — that’s why you’re writing it, after all. Using Discord partitions your community on either side of a walled garden, with one side that’s willing to use the proprietary Discord client, and one side that isn’t. It sets up users who are passionate about free software — i.e. your most passionate contributors or potential contributors — as second-class citizens.&lt;/p&gt;&lt;p&gt;By choosing Discord, you also lock out users with accessibility needs, for whom the proprietary Discord client is often a nightmare to use.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Users who cannot afford new enough hardware to make the resource-intensive client pleasant to use are also left by the wayside. Choosing Discord is a choice that excludes poor and disabled users from your community. Users of novel or unusual operating systems or devices (i.e. innovators and early adopters) are also locked out of the client until Discord sees fit to port it to their platform. Discord also declines service to users in countries under US sanctions, such as Iran. Privacy-concious users will think twice before using Discord to participate in your project, or will be denied outright if they rely on Tor or VPNs. All of these groups are excluded from your community.&lt;/p&gt;&lt;p&gt;These problems are driven by a conflict of interest between you and Discord. Ownership over your chat logs, the right to set up useful bots, or to moderate your project’s space according to your discretion; all of these are rights reserved by Discord and denied to you. The FOSS community, including users with accessibility needs or low-end computing devices, are unable to work together to innovate on the proprietary client, or to build improved clients which better suit their needs, because Discord insists on total control over the experience. Discord seeks to &lt;a href=&quot;https://seirdy.one/2021/01/27/whatsapp-and-the-domestication-of-users.html&quot; target=&quot;_blank&quot;&gt;domesticate its users&lt;/a&gt;, where FOSS treats users as peers and collaborators. These ideologies are fundamentally in conflict with one another.&lt;/p&gt;&lt;p&gt;You are making an investment when you choose to use one service over another. When you choose Discord, you are legitimizing their platform and divesting from FOSS platforms. Even if you think they have a bigger reach and a bigger audience,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; choosing them is a short-term, individualist play which signals a lack of faith in and support for the long-term goals of the FOSS ecosystem as a whole. The FOSS ecosystem needs your investment. FOSS platforms generally don’t have access to venture capital or large marketing budgets, and are less willing to use dark patterns and predatory tactics to secure their market segment. They need your support to succeed, and you need theirs. Why should someone choose to use your FOSS project when you refused to choose theirs? Solidarity and mutual support is the key to success.&lt;/p&gt;&lt;p&gt;There are great FOSS alternatives to Discord or Slack. SourceHut has been investing in IRC by building more accessible services like &lt;a href=&quot;https://sourcehut.org/blog/2021-11-29-announcing-the-chat.sr.ht-public-beta/&quot; target=&quot;_blank&quot;&gt;chat.sr.ht&lt;/a&gt;. Other great options include &lt;a href=&quot;https://matrix.org&quot; target=&quot;_blank&quot;&gt;Matrix&lt;/a&gt; and &lt;a href=&quot;https://zulip.com&quot; target=&quot;_blank&quot;&gt;Zulip&lt;/a&gt;. Please consider these services before you reach for their proprietary competitors.&lt;/p&gt;&lt;p&gt;Perceptive readers might have noticed that most of these arguments can be generalized. This article is much the same if we replace “Discord” with “GitHub”, for instance, or “Twitter” or “YouTube”. If your project depends on proprietary infrastructure, I want you to have a serious discussion with your collaborators about why. What do your choices mean for the long-term success of your project and the ecosystem in which it resides? Are you making smart investments, or just using tools which are popular or that you’re already used to?&lt;/p&gt;&lt;p&gt;If you use GitHub, consider &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; or &lt;a href=&quot;https://codeberg.org&quot; target=&quot;_blank&quot;&gt;Codeberg&lt;/a&gt;. If you use Twitter, consider &lt;a href=&quot;https://joinmastodon.org&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt; instead. If you use YouTube, try &lt;a href=&quot;https://joinpeertube.org&quot; target=&quot;_blank&quot;&gt;PeerTube&lt;/a&gt;. If you use Facebook… don’t.&lt;/p&gt;&lt;p&gt;Your choices matter. Choose wisely.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Dont-use-Discord-for-FOSS/</link>
        
        <pubDate>Tue, 28 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Dont-use-Discord-for-FOSS/</guid>
      </item>
    
      <item>
        
        
          <title>Please use me as a resource</title>
          <description>
            &lt;p&gt;I write a lot of blog posts about my ideas,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; some of which are even good ideas. Some of these ideas stick, and many readers have attempted to put them into practice, taking on challenges like starting a business in FOSS or stepping up to be leaders in their communities. It makes me proud to see the difference you’re making, and I’m honored to have inspired many of you.&lt;/p&gt;&lt;p&gt;I’m sitting here on my soapbox shouting into the void, but I also want to work with you one-on-one. Here are some things people have reached out to me for:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Pitching their project/business ideas for feedback&lt;/li&gt;&lt;li&gt;Sharing something they’re proud of&lt;/li&gt;&lt;li&gt;Cc’ing me in mailing list discussions, GitHub/GitLab threads, etc, for input&lt;/li&gt;&lt;li&gt;Clarifying finer points in my blog posts&lt;/li&gt;&lt;li&gt;Asking for feedback on drafts of their own blog posts&lt;/li&gt;&lt;li&gt;Offering philosophical arguments about FOSS&lt;/li&gt;&lt;li&gt;Asking for advice on dealing with a problem in their community&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I have my own confidants that I rely on for these same problems. None of us goes it alone, and for this great FOSS experiment to succeed, we need to rely on each other.&lt;/p&gt;&lt;p&gt;I want to be personally available to you. My email address is &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;drew@ddevault.org&lt;/a&gt;. I read every email I receive, and try to respond to most of them, though it can sometimes take a while. Please consider me a resource for your work in FOSS. I hope I can help!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Use-me-as-a-resource/</link>
        
        <pubDate>Sat, 25 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Use-me-as-a-resource/</guid>
      </item>
    
      <item>
        
        
          <title>Sustainable creativity in a world without copyright</title>
          <description>
            &lt;p&gt;I don’t believe in copyright. I argue that we need to get rid of copyright, or at least dramatically reform it. &lt;a href=&quot;https://drewdevault.com/blog/Alice-in-Wonderland/&quot;&gt;The public domain has been stolen from us&lt;/a&gt;, and I want it back. Everyone reading this post has grown up in a creative world defined by capitalism, in which adapting and remixing works — a fundamental part of the creative process — is illegal. The commons is dead, and we suffer for it. But, this is all we’ve ever known. It can be difficult to imagine a world without copyright.&lt;/p&gt;&lt;p&gt;When I present my arguments on the subject, the most frequent argument I hear in response is something like the following: “artists have to eat, too”. The answer to this argument is so mind-bogglingly obvious that, in the absence of understanding, it starkly illuminates just how successful capitalism has been in corrupting a broad human understanding of empathy. So, I will spell the answer out: why do we have a system which will, for any reason, deny someone access to food? How unbelievably cruel is a system which will let someone starve because they cannot be productive within the terms of capitalism?&lt;/p&gt;&lt;p&gt;My argument is built on the more fundamental understanding that the access to fundamental human rights such as food, shelter, security, and healthcare are not contingent on their ability to be productive under the terms of capitalism. And I emphasize the “terms of capitalism” here deliberately: how much creativity is stifled because it cannot be expressed profitably? The system is not just cruel, but it also limits the potential of human expression, which is literally the only thing that creative endeavours are concerned with.&lt;/p&gt;&lt;p&gt;The fact that the “starving artist” is such a common trope suggests to us that artists aren’t putting food on the table under the copyright regime, either. Like in many industries under capitalism, artists are often not the owners of the products of their labor. Copyright protects the rights holder, not the author. The obscene copyright rules in the United States, for example, are not doing much benefit for the artist when the term ends 70 years after their death. Modern copyright law was bought, paid for, and written by corporate copyright owners, not artists. What use is the public domain to anyone when something published today cannot be legally remixed by even our great-great-grandchildren?&lt;/p&gt;&lt;p&gt;Assume that we address both of these problems: we create an empathetic system which never denies a human being of their fundamental right to live, and we eliminate copyright. Creativity will thrive under these conditions. How?&lt;/p&gt;&lt;p&gt;Artists are free to spend their time at their discretion under the new copyright-free regime. They can devote themselves to their work without concern for whether or not it will sell, opening up richer and more experimental forms of expression. Their peers will be working on similar terms, freeing them to more frequent collaborations of greater depth. They will build upon each other’s work to create a rich commons of works and derivative works.&lt;/p&gt;&lt;p&gt;There’s no escaping the fact that derivation and remixing is a fundamental part of the creative process, and that copyright interferes with this process. Every artist remixes the works of other artists: this is how art is made. Under the current copyright regime, this practice ranges from grey-area to illegal, and because money makes right, rich and powerful artists aggressively defend their work, extracting rent from derivative works, while shamelessly ripping off works from less powerful artists who cannot afford to fight them in court. Eliminating copyright rids us of this mess and acknowledges that remixing is part of the creative process, freeing artists to build on each other’s work.&lt;/p&gt;&lt;p&gt;This is not a scenario in which artists stop making money, or in which the world grinds to a halt because no one is incentivized to work anymore. The right to have your fundamental needs met does not imply that we must provide everyone with a luxurious lifestyle. If you want a nicer house, more expensive food, to go out to restaurants and buy fancy clothes — you need to work for it. If you want to commercialize your art, you can sell CDs and books, prints or originals, tickets to performances, and so on. You can seek donations from your audience through crowdfunding platforms, court wealthy patrons of the arts, or take on professional work making artistic works like buildings and art installations for public and private sector. You could even get a side job flipping burgers or take on odd jobs to cover the costs of materials like paint or musical instruments — but not your dinner or apartment. The money you earn stretches longer, not being eaten away by health insurance or rent or electricity bills. You invest your earnings into your art, not into your livelihood.&lt;/p&gt;&lt;p&gt;Copyright is an absurd system. Ideas do not have intrinsic value. Labor has value, and goods have value. Ideas are not scarce. By making them artificially so, we sabotage the very process by which ideas are made. Copyright is illegitimate, and we can, and ought to, get rid of it.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Aside: I came across a couple of videos recently that I thought were pretty interesting and relevant to this topic. Check them out:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://redirect.invidious.io/watch?v=MZ2GuvUWaP8&quot; target=&quot;_blank&quot;&gt;Everything is a Remix Part 1 (2021), by Kirby Ferguson&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://redirect.invidious.io/watch?v=ZZ3F3zWiEmc&quot; target=&quot;_blank&quot;&gt;The Art Market is a Scam (And Rich People Run It)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Sustainable-creativity-post-copyright/</link>
        
        <pubDate>Thu, 23 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Sustainable-creativity-post-copyright/</guid>
      </item>
    
      <item>
        
        
          <title>On commercial forks of FOSS projects</title>
          <description>
            &lt;p&gt;The gaming and live streaming industry is a lucrative and rapidly growing commercial sector with a unique understanding of copyright and intellectual property, and many parties with conflicting interests and access to different economic resources.&lt;/p&gt;&lt;p&gt;The understanding of intellectual property among gamers and the companies which serve them differs substantailly from that of free software, and literacy in the values and philosophy of free software among this community is very low. It is then of little surprise that we see abuse of free software from this community, namely in the recent (and illegal) commercial forks of a popular FOSS streaming platform called &lt;a href=&quot;https://obsproject.com&quot; target=&quot;_blank&quot;&gt;OBS Studio&lt;/a&gt; by companies like TikTok.&lt;/p&gt;&lt;p&gt;These forks are in violation of the software license of OBS Studio, which is both illegal and unethical. But the “why” behind this is interesting for a number of reasons. For one, there &lt;em&gt;is&lt;/em&gt; a legitimate means through which commercial entities can repurpose free software projects, up to and including reskinning and rebranding and selling them. The gaming community also has an unusual perspective on copyright which colors their understanding of the situation. Consider, for instance, the modding community.&lt;/p&gt;&lt;p&gt;Game modifications (mods) exist in a grey area with respect to copyright. Modding in general is entirely legal, though some game companies do not understand this (or choose not to understand this) and take action against them. Modders also often use assets of dubious provenance in their work. Many people believe that, because this is all given away for free, the use is legitimate, and though they are morally correct, they are not legally correct. Additionally, since most mods are free (as in beer),&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; the currency their authors receive for their work is credit and renown. Authors of these mods tend to defend their work fiercely against its “theft”. Modders also tend to be younger, and grew up after the internet revolution and the commoditization of software.&lt;/p&gt;&lt;p&gt;On the other hand, the conditions under which free software can be “stolen” are quite different, because the redistribution, reuse, and modification of free software, including for commercial purposes, is an explicit part of the social and legal contract of FOSS. This freedom comes, however, with some conditions. The nature of these conditions varies from liberal to strict. For instance, software distributed with the MIT license requires little more than crediting the original authors in any derivative works. On the other end of this spectrum, copyleft licenses like the GPL family require that any derivative works of the original project are &lt;em&gt;also&lt;/em&gt; released under the GPL license. OBS Studio uses the GPL license, and it is in this respect that all of these forks have made a legal misstep.&lt;/p&gt;&lt;p&gt;If a company like TikTok wants to use OBS Studio to develop its own streaming software, they are &lt;em&gt;allowed to do this&lt;/em&gt;, though the degree to which they are &lt;em&gt;encouraged&lt;/em&gt; to do this is the subject of some debate.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; However, they must release the source code for their modifications under the same GPL license. They can repurpose and rebrand OBS Studio only if their repurposed and rebranded version is made available to the free software community under the same terms. Then OBS Studio can take any improvements they like from the TikTok version and incorporate them into the original OBS Studio software, so that everyone shares the benefit — TikTok, OBS users, StreamLabs, and StreamElements alike, as well as anyone else who wants in on the game.&lt;/p&gt;&lt;p&gt;This happens fairly often with free software and often forms a healthy relationship by establishing an incentive and a pool of economic resources to provide for the upkeep and development of that software. Many developers of a project like this are often hired by such companies to do their work. Sometimes, this relationship is viewed more negatively, but that’s a subject for another post. It works best when all of the players view each other as collaborators, not competitors.&lt;/p&gt;&lt;p&gt;That’s not what happening here, though. What we’re seeing instead is the brazen theft of free software by corporations who believe that, because their legal budget exceeds the resources available to the maintainers, might makes right.&lt;/p&gt;&lt;p&gt;Free software is designed to be used commercially, but you have to do it correctly. This is a resource which is made available to companies who want to exploit it, but they must do so according to the terms of the licenses. It’s not a free lunch.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Commercial-forks-of-FOSS-projects/</link>
        
        <pubDate>Sat, 18 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Commercial-forks-of-FOSS-projects/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, December 2021</title>
          <description>
            &lt;p&gt;Greetings! It has been a cold and wet month here in Amsterdam, much like the rest of them, as another period of FOSS progress rolls on by. I have been taking it a little bit easier this month, and may continue to take some time off in the coming weeks, so I can have a bit of a rest for the holidays. However, I do have some progress to report, so let’s get to it.&lt;/p&gt;&lt;p&gt;In programming language progress, we’ve continued to see improvement in cryptography, with more AES cipher modes and initial work on AES-NI support for Intel processors, as well as support for HMAC and blake2b. Improved support for linking with C libraries has also landed, which is the basis of a few third-party libraries which are starting to appear, such as bindings to &lt;a href=&quot;https://github.com/andlabs/libui&quot; target=&quot;_blank&quot;&gt;libui&lt;/a&gt;. I have also started working on bindings to SDL2, which I am using to make a little tetromino game (audio warning):&lt;/p&gt;&lt;p&gt;&lt;video src=&quot;https://redacted.moe/f/182e81ce.webm&quot; controls&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;I am developing this to flesh out the SDL wrapper and get a feel for game development in the new language, but I also intend to take it on as a serious project to make a game which is fun to play. I also started working on an IRC protocol library for our language, but this does not link to C.&lt;/p&gt;&lt;p&gt;Also, the reflection support introduced a few months ago has been removed.&lt;/p&gt;&lt;p&gt;My other main focus has been SourceHut, where I have been working on todo.sr.ht’s GraphQL API. This one ended up being a lot of work. I expect to require another week or two to finish it.&lt;/p&gt;&lt;p&gt;visurf also enjoyed a handful of improvements this month, thanks to some contributors, the most prolific of whom was Pranjal Kole. Thanks Pranjal! Improvements landed this month include tab rearranging, next and previous page navigation, and an improvement to all of the new-tab logic, along with many bug fixes and smaller improvements. I also did some of the initial work on command completions, but there is a lot left to do in this respect.&lt;/p&gt;&lt;p&gt;That’s all for today. Thanks for your continued support! Until next time.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-December-2021/</link>
        
        <pubDate>Wed, 15 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-December-2021/</guid>
      </item>
    
      <item>
        
        
          <title>Impressions of Linux Mint &amp; elementary OS</title>
          <description>
            &lt;p&gt;In a &lt;a href=&quot;https://drewdevault.com/2021/12/05/What-desktop-Linux-needs.html&quot; target=&quot;_blank&quot;&gt;recent post&lt;/a&gt;, I spoke about some things that Linux distros need to do better to accommodate end-users. I was reminded that there are some Linux distros which are, at least to some extent, following my recommended playbook, and have been re-evaluating two of them over the past couple of weeks: &lt;a href=&quot;https://linuxmint.com&quot; target=&quot;_blank&quot;&gt;Linux Mint&lt;/a&gt; and &lt;a href=&quot;https://elementary.io&quot; target=&quot;_blank&quot;&gt;elementary OS&lt;/a&gt;. I installed these on one of my laptops and used it as my daily driver for a day or two each.&lt;/p&gt;&lt;p&gt;Both of these distributions are similar in a few ways. For one, both distros required &lt;em&gt;zero&lt;/em&gt; printer configuration: it just worked. I was very impressed with this. Both distros are also based on Ubuntu, though with different levels of divergence from their base. Ubuntu is a reasonably good choice: it is very stable and mature, and commercially supported by Canonical.&lt;/p&gt;&lt;p&gt;I started with elementary OS, which does exactly what I proposed in my earlier article: charge users for the OS.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; The last time I tried elementary, I was less than impressed, but they’ve been selling the OS for a while now so I hoped that with a consistent source of funding and a few years to improve they would have an opportunity to impress me. However, my overall impressions were mixed, and maybe even negative.&lt;/p&gt;&lt;p&gt;The biggest, showstopping issue is a problem with their full disk encryption setup. I was thrilled to see first-class FDE support in the installer, but upon first boot, I was presented with a blank screen. It took me a while to figure out that a different TTY had cryptsetup running, waiting for me to enter the password. This is &lt;em&gt;totally&lt;/em&gt; unacceptable, and no average user would have any clue what to do when presented with this. This should be a little GUI baked into the initramfs which prompts for your password on boot, and should be a regularly tested part of the installer before each elementary release ships.&lt;/p&gt;&lt;p&gt;The elementary store was also disappointing, though I think there’s improvements on the horizon. The catalogue is &lt;em&gt;very&lt;/em&gt; sparse, and would benefit a lot by sourcing packages from the underlying Ubuntu repositories as well. I think they’re planning on a first-class Flatpak integration in a future release, which should improve this situation. I also found the apps a bit &lt;em&gt;too&lt;/em&gt; elementary, haha, in that they were lacking in a lot of important but infrequently used features. In general elementary is quite basic, though it is also very polished. Also, the default wallpaper depicts a big rock covered in bird shit, which I thought was kind of funny.&lt;/p&gt;&lt;p&gt;There is a lot to like about elementary, though. The installer is really pleasant to use, and I really appreciated that it includes important accessibility features during the install process. The WiFi configuration is nice and easy, though it prompted me to set up online accounts &lt;em&gt;before&lt;/em&gt; prompting me to set up WiFi. All of the apps are intuitive, consistently designed, and beautiful. I also noticed that long-running terminal processes I had in the background would pop-up a notification upon completion, which is a nice touch. Overall, it’s promising, but I had hoped for more. My suggestions to elementary are to consider that completeness is a kind of polish, to work on software distribution, and to offer first-class options for troubleshooting, documentation, and support within the OS.&lt;/p&gt;&lt;p&gt;I tried Linux Mint next. Several years ago, I actually used Mint as my daily driver for about a year — it was the last “normal” distribution I used before moving to Arch and later Alpine, which is what I use now. Overall, I was pretty impressed with Mint after a couple of days of use.&lt;/p&gt;&lt;p&gt;Let’s start again with the bad parts. The installer is not quite as nice as elementary’s, though it did work without any issues. At one point I was asked if I wanted to “enable multimedia codecs” with no extra context, which would confuse me if I didn’t understand what they were. I was also pretty pissed to see the installer advertising nonfree, predatory services like Netflix and YouTube to me — distributions have no business advertising this kind of shit. Mint also has encryption options, but it’s based on ecryptfs rather than LUKS, and I find that this is an inferior approach. Mint should move to full-disk encryption.&lt;/p&gt;&lt;p&gt;I also was a bit concerned about the organizational structure of Linux Mint. It’s unclear who is responsible for Linux Mint, how end-users can participate, or how donations are spent or how other financial concerns are addressed. I think that Linux Mint needs to be more transparent, and should also consider how its allegiance with proprietary services like Netflix acts as a long-term divestment from the FOSS ecosystem it relies on.&lt;/p&gt;&lt;p&gt;That said, the actual experience of using Linux Mint is very good. Unlike elementary OS, the OS feels much more &lt;em&gt;comprehensive&lt;/em&gt;. Most of the things a typical user would need are there, work reliably, and integrate well with the rest of the system. Software installation and system upkeep are very easy on Linux Mint. The aesthetic is very pleasant and feels like a natural series of improvements to the old Gnome 2 lineage that Cinnamon can be traced back to, which has generally moved more in the direction that I would have liked Gnome upstream to. The system is tight, complete, and robust. Nice work.&lt;/p&gt;&lt;p&gt;In conclusion, Linux Mint will be my recommendation for “normal” users going forward, and I think there is space for elementary OS for some users if they continue to improve.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Linux-Mint-and-elementary-OS/</link>
        
        <pubDate>Tue, 14 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Linux-Mint-and-elementary-OS/</guid>
      </item>
    
      <item>
        
        
          <title>What desktop Linux needs to succeed in the mainstream</title>
          <description>
            &lt;p&gt;The &lt;a href=&quot;https://redirect.invidious.io/channel/UCXuqSBlHAE6Xw-yeJA0Tunw&quot; target=&quot;_blank&quot;&gt;Linus Tech Tips&lt;/a&gt; YouTube channel has been putting out a series of videos called the &lt;a href=&quot;https://redirect.invidious.io/playlist?list=PL8mG-RkN2uTyhe6fxWpnsHv53Y1I-K3yu&quot; target=&quot;_blank&quot;&gt;Switching to Linux Challenge&lt;/a&gt; that has been causing a bit of a stir in the Linux community. I’ve been keeping an eye on these developments, and thought it was a good time to weigh in with my thoughts. This article focuses on what Linux needs to do better — I have also written a companion article, “&lt;a href=&quot;https://drewdevault.com/blog/How-new-Linux-users-succeed/&quot;&gt;How new Linux users can increase their odds of success&lt;/a&gt;”, which looks at the other side of the problem.&lt;/p&gt;&lt;p&gt;Linux is &lt;em&gt;not&lt;/em&gt; accessible to the average user today, and I didn’t need to watch these videos to understand that. I do not think that it is reasonable today to expect a non-expert user to successfully install and use Linux for their daily needs without a “Linux friend” holding their hand every step of the way.&lt;/p&gt;&lt;p&gt;This is not a problem unless we want it to be. It is entirely valid to build software which is accommodating of experts only, and in fact this is the kind of software I focus on in my own work. I occasionally use the racecar analogy: you would not expect the average driver to be able to drive a Formula 1 racecar. It is silly to suggest that Formula 1 vehicle designs ought to accommodate non-expert drivers, or that professional racecar drivers should be driving mini-vans on the circuit. However, it is equally silly to design a professional racing vehicle and market it to soccer moms.&lt;/p&gt;&lt;p&gt;I am one of the original developers of the &lt;a href=&quot;https://swaywm.org&quot; target=&quot;_blank&quot;&gt;Sway&lt;/a&gt; desktop environment for Linux. I am very proud of Sway, and I believe that it represents one of the best desktop experiences on Linux. It is a rock-solid, high-performance, extremely stable desktop which is polished on a level that is competitive with commercial products. However, it is designed for &lt;em&gt;me&lt;/em&gt;: a professional, expert-level Linux user. I am under no illusions that it is suitable for my grandmother.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;This scenario is what the incentives of the Linux ecosystem favors most. Linux is one of &lt;em&gt;the best&lt;/em&gt; operating systems for professional programmers and sysadmins, to such an extraordinary degree that most programmers I know treat Windows programmers and sysadmins as the object of well-deserved ridicule. Using Windows for programming or production servers is essentially as if the race car driver from my earlier metaphor &lt;em&gt;did&lt;/em&gt; bring a mini-van to the race track. Linux is the operating system developed by programmers, for programmers, to suit &lt;em&gt;our&lt;/em&gt; needs, and we have succeeded tremendously in this respect.&lt;/p&gt;&lt;p&gt;However, we have failed to build an operating system for people who are &lt;em&gt;not&lt;/em&gt; like us.&lt;/p&gt;&lt;p&gt;If this is not our goal, then that’s fine. But, we can build things for non-experts if we choose to. If we set “accessible to the average user” as a goal, then we must take certain steps to achieve it. We need to make major improvements in the following areas: robustness, intuitiveness, and community.&lt;/p&gt;&lt;p&gt;The most frustrating moments for a user is when the software they’re using does something inexplicable, and it’s these moments that they will remember the most vividly as part of their experience. Many Linux desktop and distribution projects are spending their time on shiny new features, re-skins, and expanding their scope further and further. This is a fool’s errand when the project is not reliable at its current scope. A small, intuitive, reliable program is better than a large, unintuitive, unreliable program. Put down the paint brush and pick up the polishing stone. I’m looking at you, KDE.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;A user-friendly Linux desktop system should not crash. It should not be possible to install a package which yeets gnome-desktop and dumps them into a getty. The smallest of interactions must be intuitive and reliable, so that when Linus drags files from the decompression utility into a directory in Dolphin, it does the right thing. This will require a greater degree of cooperation and unity between desktop projects. Unrelated projects with common goals need to be reaching out to one another and developing robust standards for achieving those goals. I’m looking at you, Gnome.&lt;/p&gt;&lt;p&gt;Linux is a box of loosely-related tools held together with staples and glue. This is fine when the user understands the tools and is holding the glue bottle, but we need to make a more cohesive, robust, and reliable system out of this before it can accommodate average end-users.&lt;/p&gt;&lt;p&gt;We also have a lot of work to do in the Linux community. The discussion on the LTT video series has been exceptionally toxic and downright embarrassing. There is a major problem of elitism within the Linux community. Given a hundred ways of doing things on Linux (✓), there will be 99 assholes ready to tell you that your way sucks (✓). Every Linux user is responsible for doing better in this regard, especially the moderators of Linux-adjacent online spaces. Wouldn’t it be better if we took pride in being a friendly, accessible community? Don’t flame the noobs.&lt;/p&gt;&lt;p&gt;Don’t flame the experts, either. When Pop!_OS removed gnome-desktop upon installing Steam, the Linux community rightly criticised them for it. This was a major failure mode of the system in one of its flagship features, and should have never shipped. It illuminates systemic failures in the areas I have drawn our attention to in this article such as robustness and intuitiveness, and Pop!_OS is responsible for addressing the problem. None of that excuses the toxic garbage which was shoveled into the inboxes of Pop!_OS developers and users. Be better people.&lt;/p&gt;&lt;p&gt;Beyond the toxicity, there are further issues with the Linux community. There are heaps and heaps of blogs shoveling out crappy non-solutions to problems noobs might be Googling, most of which will fuck up their Linux system in some way or another. It’s very easy to find bad advice for Linux, and very hard to find good advice for Linux. The blog spammers need to cut it out, and we need to provide better, more accessible resources for users to figure out their issues. End-user-focused Linux distributions need to take responsibility for making certain that their users understand the best ways to get help for any issues they run into, so they don’t go running off to the Arch Linux forums blindly running terminal commands which will break their Ubuntu installation.&lt;/p&gt;&lt;p&gt;End-user software also needs to improve in this respect. In the latest LTT video, Luke wanted to install OBS, and the right thing to do was install it from their package manager. However, &lt;a href=&quot;https://obsproject.com/download&quot; target=&quot;_blank&quot;&gt;the OBS website&lt;/a&gt; walks them through installing a PPA instead, and has a big blue button for building it from source, which is definitely not what an average end-user should be doing.&lt;/p&gt;&lt;p&gt;→ Related: &lt;a href=&quot;https://drewdevault.com/blog/Let-distros-do-their-job/&quot;&gt;Developers: Let distros do their job&lt;/a&gt;&lt;/p&gt;&lt;p&gt;One thing that we do not need to do is “be more like Windows”, or any other OS. I think that this is a common fallacy found in end-user Linux software. We should develop a system which is intuitive in its own right without having to crimp off of Windows. Let’s focus on what makes Linux interesting and useful, and try to build a robust, reliable system which makes those interesting and useful traits accessible to users. Chasing after whatever Windows does is not the right thing to do. Let’s be prepared to ask users to learn things like new usability paradigms if it illuminates a better way of doing things.&lt;/p&gt;&lt;p&gt;So, these are the goals. How do we achieve them?&lt;/p&gt;&lt;p&gt;I reckon that we could use a commercial, general-purpose end-user Linux distro. As I mentioned earlier, the model of developers hacking in their spare time to make systems for themselves does not create incentives which favor the average end-user. You can sell free software — someone ought to do so! Build a commercial Linux distro, charge $20 to download it or mail an install CD to the user, and invest that money in developing a better system and offer dedicated support resources. Sure, it’s &lt;em&gt;nice&lt;/em&gt; that Linux is free-as-in-beer, but there’s no reason it has to be. I’ve got &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;my own business&lt;/a&gt; to run, so I’ll leave that as &lt;a href=&quot;https://stripe.com/atlas&quot; target=&quot;_blank&quot;&gt;an exercise for the reader&lt;/a&gt;. Good luck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/What-desktop-Linux-needs/</link>
        
        <pubDate>Sun, 05 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/What-desktop-Linux-needs/</guid>
      </item>
    
      <item>
        
        
          <title>How new Linux users can increase their odds of success</title>
          <description>
            &lt;p&gt;The &lt;a href=&quot;https://redirect.invidious.io/channel/UCXuqSBlHAE6Xw-yeJA0Tunw&quot; target=&quot;_blank&quot;&gt;Linus Tech Tips&lt;/a&gt; YouTube channel has been putting out a series of videos called the &lt;a href=&quot;https://redirect.invidious.io/playlist?list=PL8mG-RkN2uTyhe6fxWpnsHv53Y1I-K3yu&quot; target=&quot;_blank&quot;&gt;Switching to Linux Challenge&lt;/a&gt; that has been causing a bit of a stir in the Linux community. I’ve been keeping an eye on these developments, and thought it was a good time to weigh in with my thoughts. This article focuses on how new Linux users can increase their odds for success — I have also written a companion article, “&lt;a href=&quot;https://drewdevault.com/blog/What-desktop-Linux-needs/&quot;&gt;What desktop Linux needs to succeed in the mainstream&lt;/a&gt;”, which looks at the other side of the problem.&lt;/p&gt;&lt;p&gt;Linux is, strictly speaking, an operating system &lt;em&gt;kernel&lt;/em&gt;, which is a small component of a larger system. However, in the common usage, Linux refers to a family of operating systems which are based on this kernel, such as Ubuntu, Fedora, Arch Linux, Alpine Linux, and so on, which are referred to as &lt;em&gt;distributions&lt;/em&gt;. Linux is used in other contexts, such as Android, but the common usage is generally limited to this family of Linux “distros”. Several of these distros have positioned themselves for various types of users, such as office workers or gamers. However, the most common Linux user is much different. What do they look like?&lt;/p&gt;&lt;p&gt;The key distinction which sets Linux apart from more common operating systems like Windows and macOS is that Linux is &lt;em&gt;open source&lt;/em&gt;. This means that the general public has access to the &lt;em&gt;source code&lt;/em&gt; which makes it tick, and that anyone can modify it or improve it to suit their needs. However, to make meaningful modifications to Linux requires programming skills, so, consequentially, the needs which Linux best suits are the needs of programmers. Linux is the preeminent operating system for programmers and other highly technical computer users, for whom it can be suitably molded to purpose in a manner which is not possible using other operating systems. As such, it has been a resounding success on programmer’s workstations, on servers in the cloud, for data analysis and science, in embedded workloads like internet-of-things, and other highly technical domains where engineering talent is available and a profound level of customization is required.&lt;/p&gt;&lt;p&gt;The Linux community has also developed Linux as a solution for desktop users, such as the mainstream audience of Windows and macOS. However, this work is mostly done by enthusiasts, rather than commercial entities, so it can vary in quality and generally any support which is available is offered on a community-run, best-effort basis. Even so, there have always been a lot of volunteers interested in this work — programmers want a working desktop, too. Programmers also want to play games, so there has been interest in getting a good gaming setup working on Linux. In the past several years, there has also been a commercial interest with the budget to move things forward: Valve Software. Valve has been instrumental in developing more sophisticated gaming support on Linux, and uses Linux as the basis of a commercial product, the Steam Deck.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Even so, I must emphasize the following point:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The best operating system for gaming is Windows.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Trying to make Linux do all of the things you’re used to from Windows or macOS is not going to be a successful approach. It is &lt;em&gt;possible&lt;/em&gt; to run games on Linux, and it is &lt;em&gt;possible&lt;/em&gt; to run some Windows software on Linux, but it is not &lt;em&gt;designed&lt;/em&gt; to do these things, and you will likely encounter some papercuts on the way. Many advanced Linux users with a deep understanding of the platform and years of experience under their belt can struggle for days to get a specific game running. However, thanks to Valve, and the community at large, many games — but not all games — run out-of-the-box with much less effort than was once required of Linux gamers.&lt;/p&gt;&lt;p&gt;Linux users are excited about improved gaming support because it brings gaming to a platform that they already want to use &lt;em&gt;for other reasons&lt;/em&gt;. Linux is not Windows, and offers an inferior gaming experience to Windows, but it &lt;em&gt;does&lt;/em&gt; offer a superior experience in many other regards! If you are trying out Linux, you should approach it with an open mind, prepared to learn about what makes Linux special and &lt;em&gt;different&lt;/em&gt; from Windows. You’ll learn about new software, new usability paradigms, and new ways of using your computer. If you just want to do all of the same things on Linux that you’re already doing on Windows, why switch in the first place? The value of Linux comes from what it can do differently. Given time, you will find that there are many things that Linux can do that Windows cannot. Leave your preconceptions at the door and seek to learn what makes Linux special.&lt;/p&gt;&lt;p&gt;I think that so-called “power users” are especially vulnerable to this trap, and I’ve seen it happen many times. A power user is someone who deeply understands the system that they’re using, knows about every little feature, knows all of the keyboard shortcuts, and integrates all of these details into their daily workflow. Naturally, it will take you some time to get used to a new system. You can be a power user on Linux — I am one such user myself — but you’re essentially starting from zero, and you will learn about different features, different nuances, and different shortcuts, all of which ultimately sums to an entirely &lt;em&gt;different&lt;/em&gt; power user.&lt;/p&gt;&lt;p&gt;The latest LTT video in the Linux series shows the team going through a set of common computer tasks on Linux. However, these tasks do little to nothing to show off what makes Linux special. Watching a 4K video is nice, sure, and you can do it on Linux, but how does that teach you anything interesting about Linux?&lt;/p&gt;&lt;p&gt;Let me offer a different list of challenges for a new Linux user to attempt, hand-picked to show off the things which set Linux &lt;em&gt;apart&lt;/em&gt; in my opinion.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Learn how to use the shell.&lt;/strong&gt; A lot of new Linux users are intimidated by the terminal, and a lot of old Linux users are understandably frustrated about this. The terminal is one of the &lt;em&gt;best&lt;/em&gt; things about Linux! We praise it for a reason, intimidating as it may be. &lt;a href=&quot;https://ubuntu.com/tutorials/command-line-for-beginners#1-overview&quot; target=&quot;_blank&quot;&gt;Here’s a nice tutorial to start with&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Find and install packages from the command line.&lt;/strong&gt; On Linux, you install software by using a “package manager”, a repository of software controlled by the Linux distribution. Think of it kind of like an app store, but non-commercial and without malware, adware, or spyware. If you are downloading Linux software from a random website, it’s probably the wrong thing to do. See if you can figure out the package manager instead!&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Try out a tiling window manager,&lt;/strong&gt; especially if you consider yourself a power user. I would recommend &lt;a href=&quot;https://swaywm.org&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt;, though I’m biased because I started this project. Tiling window managers change the desktop usability paradigm by organizing windows &lt;em&gt;for you&lt;/em&gt; and letting you navigate and manipulate them using keyboard shortcuts alone. These are big productivity boosters.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Compile a program from source.&lt;/strong&gt; This generally is not how you will usually find and use software, but it is an interesting experience that you cannot do on Windows or Mac. Pick something out and figure out where the source code is and how to compile it yourself. Maybe you can make a little change to it, too!&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Help someone else out online.&lt;/strong&gt; Linux is a community of volunteers supporting each other. Take what you’ve learned to &lt;a href=&quot;https://reddit.com/r/linuxquestions&quot; target=&quot;_blank&quot;&gt;/r/linuxquestions&lt;/a&gt; or your distro’s chat rooms, forums, wikis, or mailing lists, and make them a better place for everyone else. The real magic of Linux comes from the collaborative, grassroots nature of the project, which is something you really cannot get from Windows or Mac.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Bonus challenge: complete all of the challenges from the LTT video, but only using the command line.&lt;/p&gt;&lt;p&gt;All of these tasks might take a lot longer than 15 minutes to do, but remember: embrace the unfamiliar. You don’t learn anything by doing the things you already know how to do. If you want to know why Linux is special, you’ll have to step outside of your comfort zone. Linux is free, so there’s no risk in trying 🙂 Good luck, and do not be afraid to ask for help if you get stuck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-new-Linux-users-succeed/</link>
        
        <pubDate>Sun, 05 Dec 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-new-Linux-users-succeed/</guid>
      </item>
    
      <item>
        
        
          <title>postmarketOS revolutionizes smartphone hacking</title>
          <description>
            &lt;p&gt;I briefly mentioned &lt;a href=&quot;http://postmarketos.org/&quot; target=&quot;_blank&quot;&gt;postmarketOS&lt;/a&gt; in &lt;a href=&quot;https://drewdevault.com/2019/12/18/PinePhone-review.html&quot; target=&quot;_blank&quot;&gt;my Pinephone review&lt;/a&gt; two years ago, but after getting my Dutch SIM card set up in my Pinephone and having another go at using postmarketOS, I reckon they deserve special attention.&lt;/p&gt;&lt;p&gt;Let’s first consider the kind of ecosystem into which postmarketOS emerged: smartphone hacking in the XDA Forums era. This era was dominated by amateur hackers working independently for personal prestige, with little to no regard for the values of free software or collaboration. It was common to see hacked-together binary images shipped behind adfly links in XDA forum threads in blatant disregard of the GPL, with pages and pages of users asking redundant questions and receiving poor answers to the endless problems caused by this arrangement.&lt;/p&gt;&lt;p&gt;The XDA ecosystem is based on Android, which is a mess in and of itself. It’s an enormous, poorly documented ball of Google code, mixed with vendor drivers and private kernel trees, full of crappy workarounds and locked-down hardware. Most smart phones are essentially badly put-together black boxes and most smart phone hackers are working with their legs cut off. Not to mention that the software ecosystem which runs on the platform is full of scammers and ads and theft of private user information. Android may be Linux in implementation, but it’s about as far from the spirit of free software as you can get.&lt;/p&gt;&lt;p&gt;postmarketOS, on the other hand, is based on Alpine Linux, which happens to be my favorite Linux distribution. Instead of haphazard forum threads collecting inscrutable ports for dozens of devices, they have a single git repository where all of their ports are maintained under version control, complete with issue trackers and merge requests, plus a detailed centralized wiki providing a wealth of open technical info on their supported platforms. And, by virtue of being a proper Linux distribution, they essentially opt-out of the mess of predatory mobile apps and instead promote a culture of trusted applications which respect the user and are built by and for the community instead of by and for a corporation.&lt;/p&gt;&lt;p&gt;Where we once had to live with illegally closed-source forks of the Linux kernel, we now have a git repository in which upstream Linux releases are tracked with a series of auditable patches for supporting various devices, many of which are making their way into upstream Linux. Where we once had a forum thread with five wrong answers to the same question on page 112, we now have a bug report on GitLab with a documented workaround and a merge request pending review. Instead of begging my vendor to unlock my bootloader and using janky software reminiscent of old keygen hacks to flash a dubious Android image, I can build postmarketOS’s installer, pop it onto a microSD card, and two minutes I’ll have Linux installed on my Pinephone.&lt;/p&gt;&lt;p&gt;pmOS does not seek to elevate the glories of tiny individual hackers clutching their secrets close to their chest, instead elevating the glory of the community as a whole. It pairs perfectly with Pine64, the only hardware vendor working closely with upstream developers with the same vision and ideals. There is a promise for hope in the future of smart phones in their collaboration.&lt;/p&gt;&lt;p&gt;However, the path they’ve chosen is a difficult one. Android, for all of its faults, presents a complete solution for a mobile operating system, and upstream Linux does not. In my review, I said that software would be the biggest challenge of the Pinephone, and 2 years later, that remains the case. Work reverse engineering the Pine64 hardware is slow, there is not enough cooperation between project silos, and there needs to be much better prioritization of the work. To complete their goals, the community will have to work more closely together and narrow their attention in on the key issues which stand between the status quo and the completion of a useful Linux smartphone. It will require difficult, boring engineering work, and will need the full attention and dedication of the talented people working on these projects.&lt;/p&gt;&lt;p&gt;If they succeed in spite of these challenges, the results will be well worth it. postmarketOS and pine64 represent the foundations of a project which could finally deliver Linux on smartphones and build a robust mobile platform that offers freedom to its users for years to come.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/postmarketos/</link>
        
        <pubDate>Fri, 26 Nov 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/postmarketos/</guid>
      </item>
    
      <item>
        
        
          <title>My philosophy for productive instant messaging</title>
          <description>
            &lt;p&gt;We use Internet Relay Chat (&lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Relay_Chat&quot; target=&quot;_blank&quot;&gt;IRC&lt;/a&gt;) extensively at &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;sourcehut&lt;/a&gt; for real-time group chats and one-on-one messaging. The IRC protocol is quite familiar to hackers, who have been using it since the late 80’s. As chat rooms have become more and more popular among teams of both hackers and non-hackers in recent years, I would like to offer a few bites of greybeard wisdom to those trying to figure out how to effectively use instant messaging for their own work.&lt;/p&gt;&lt;p&gt;For me, IRC is a vital communication tool, but many users of &lt;insert current instant messaging software fad here&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; find it frustrating, often to the point of resenting the fact that they have to use it at all. Endlessly catching up on discussions they missed, having their workflow interrupted by unexpected messages, searching for important information sequestered away in a discussion which happened weeks ago… it can be overwhelming and ultimately reduce your productivity and well-being. Why does it work for me, but not for them? To find out, let me explain how I think about and use IRC.&lt;/p&gt;&lt;p&gt;The most important trait to consider when using IM software is that it is &lt;em&gt;ephemeral&lt;/em&gt;, and must be treated as such. You should not “catch up” on discussions that you missed, and should not expect others to do so, either. Any important information from a chat room discussion must be moved to a more permanent medium, such as an email to a mailing list,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; a ticket filed in a bug tracker, or a page updated on a wiki. One very productive use of IRC for me is holding a discussion to hash out the details of an issue, then writing up a summary up for a mailing list thread where the matter is discussed in more depth.&lt;/p&gt;&lt;p&gt;I don’t treat discussions on IRC as actionable until they are shifted to another mode of discussion. On many occasions, I have discussed an issue with someone on IRC, and once the unknowns are narrowed down and confirmed to be actionable, ask them to follow-up with an email or a bug report. If the task never leaves IRC, it also never gets done.  Many invalid or duplicate tasks are filtered out by this approach, and those which do get mode-shifted often have more detail than they otherwise might, which improves the signal-to-noise ratio on my bug trackers and mailing lists.&lt;/p&gt;&lt;p&gt;I have an extensive archive of IRC logs dating back over 10 years, tens of gigabytes of gzipped plaintext files. I reference these logs perhaps only two or three times a year, and often for silly reasons, like finding out how many swear words were used over some time frame in a specific group chat, or to win an argument about who was the first person to say “yeet” in my logs. I almost never read more than a couple dozen lines of the backlog when starting up IRC for the day.&lt;/p&gt;&lt;p&gt;Accordingly, you should never expect anyone to be in the know for a discussion they were not present at. This also affects how I use “highlights”.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Whenever I highlight someone, I try to include enough context in the message so that they can understand why they were mentioned without having to dig through their logs, even if they receive the notification hours later.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Bad&lt;/em&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;sircmpwn&amp;gt; minus: ping
&amp;lt;sircmpwn&amp;gt; what is the best way to frob foobars?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Good&lt;/em&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;sircmpwn&amp;gt; minus: do you know how to frob foobars?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I will also occasionally send someone a second highlight un-pinging them if the question was resolved and their input is no longer needed. Sometimes I &lt;em&gt;will&lt;/em&gt; send a vague “ping &lt;username&gt;” example when I actually want them to participate in the discussion &lt;em&gt;right now&lt;/em&gt;, but if they don’t answer immediately then I will usually un-ping them later.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;This draws attention to another trait of instant messaging: it is &lt;em&gt;asynchronous&lt;/em&gt;. Not everyone is online at the same time, and we should adjust our usage of it in consideration of this. For example, when I send someone a private message, rather than expecting them to engage in a real-time dialogue with me right away, I dump everything I know about the issue for them to review and respond to in their own time. This could be hours later, when I’m not available myself!&lt;/p&gt;&lt;p&gt;&lt;em&gt;Bad&lt;/em&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;sircmpwn&amp;gt; hey emersion, do you have a minute?
*8 hours later*
&amp;lt;emersion&amp;gt; yes?
*8 hours later*
&amp;lt;sircmpwn&amp;gt; what is the best way to frob foobars?
*8 hours later*
&amp;lt;emersion&amp;gt; did you try mongodb?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Good&lt;/em&gt;:&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;sircmpwn&amp;gt; hey emersion, what&amp;apos;s the best way to frob foobars?
&amp;lt;sircmpwn&amp;gt; I thought about mongodb but they made it non-free
*10 minutes later*
&amp;lt;sircmpwn&amp;gt; update: considered redis, but I bet they&amp;apos;re one bad day away from making that non-free too
*8 hours later*
&amp;lt;emersion&amp;gt; good question
&amp;lt;emersion&amp;gt; maybe postgresql? they seem like a trustworthy bunch
*8 hours later*
&amp;lt;sircmpwn&amp;gt; makes sense. Thanks!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This also presents us a solution to the interruptions problem: just don’t answer right away, and don’t expect others to. I don’t have desktop or mobile notifications for IRC. I only use it when I’m sitting down at my computer, and I “pull” notifications from it instead of having it “push” them to me — that is, I glance at the client every now and then. If I’m in the middle of something, I don’t read it.&lt;/p&gt;&lt;p&gt;With these considerations in mind, IRC has been an extraordinarily useful tool for me, and maybe it can be for you, too. I’m not troubled by interruptions to my workflow. I never have to catch up on a bunch of old messages. I can communicate efficiently and effectively with my team, increasing our productivity considerably, without worrying about an added source of stress. I hope that helps!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-philosophy-for-instant-messaging/</link>
        
        <pubDate>Wed, 24 Nov 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-philosophy-for-instant-messaging/</guid>
      </item>
    
      <item>
        
        
          <title>Python: Please stop screwing over Linux distros</title>
          <description>
            &lt;blockquote&gt;&lt;p&gt;Linux distributions? Oh, those things we use to bootstrap our Docker containers? Yeah, those are annoying. What were you complaining about again?&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The Python community is obsessed with reinventing the wheel, over and over and over and over and over and over again. distutils, setuptools, pip, pipenv, tox, flit, conda, poetry, virtualenv, requirements.txt, setup.py, setup.cfg, pyproject.toml… I honestly can’t even list all of the things you have to deal with. It’s a disaster.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/1987/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/python_environment.png&quot;&gt;
&lt;figcaption&gt;An xkcd comic showing a convoluted graph of competing Python environments&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;This comic is almost 4 years old and it has become much worse since. Python is a mess. I really want to like Python. I have used it for many years and in many projects, including SourceHut, which was predominantly developed in Python. But I simply can’t handle it anymore, and I have been hard at work removing Python from my stack.&lt;/p&gt;&lt;p&gt;This has always been a problem with Python, but in the past few years everyone and their cousin decided to “solve” it by building another mess which is totally incompatible with all of the others, all of the “solutions” enjoying varying levels of success in the community and none of them blessed as the official answer.&lt;/p&gt;&lt;p&gt;I manage my Python packages in the only way which I think is sane: installing them from my Linux distribution’s package manager. I maintain a few dozen Python packages for Alpine Linux myself. It’s from this perspective that, throughout all of this turmoil in Python’s packaging world, I have found myself feeling especially put out.&lt;/p&gt;&lt;p&gt;Every one of these package managers is designed for a reckless world in which programmers chuck packages wholesale into ~/.pip, set up virtualenvs and pin their dependencies to 10 versions and 6 vulnerabilities ago, and ship their computers directly into production in Docker containers which aim to do the minimum amount necessary to make their user’s private data as insecure as possible.&lt;/p&gt;&lt;p&gt;None of these newfangled solutions addresses the needs of any of the distros, despite our repeated pleas. They all break backwards compatibility with our use-case and send our complaints to /dev/null. I have seen representatives from every Linux distro making repeated, desperate pleas to Python to address their concerns, from Debian to Arch to Alpine to NixOS, plus non-Linux distros like FreeBSD and Illumos. Everyone is frustrated. We are all struggling to deal with Python right now, and Python is not listening to us.&lt;/p&gt;&lt;p&gt;What is it about Linux distros that makes our use-case unimportant? Have we offered no value to Python over the past 30 years? Do you just feel that it’s time to shrug off the “legacy” systems we represent and embrace the brave new world of serverless cloud-scale regulation-arbitrage move-fast-and-break-things culture of the techbro startup?&lt;/p&gt;&lt;p&gt;Distros are feeling especially frustrated right now, but I don’t think we’re alone. Everyone is frustrated with Python packaging. I call on the PSF to sit down for some serious, sober engineering work to fix this problem. Draw up a list of the use-cases you need to support, pick the most promising initiative, and put in the hours to make it work properly, today and tomorrow. Design something you can stick with and make stable for the next 30 years. If you have to break some hearts, fine. Not all of these solutions can win. Right now, upstream neglect is destroying the Python ecosystem. The situation is grave, and we need strong upstream leadership right now.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;P.S. PEP-517 and 518 are a start, but are very disappointing in how little they address distro problems. These PEPs are designed to tolerate the proliferation of build systems, which is exactly what needs to stop. Python ought to stop trying to avoid hurting anyone’s feelings and pick one. Maybe their decision-making framework prevents this, if so, the framework needs to be changed.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;P.P.S. There are a lot of relevant xkcds that I wanted to add. Here’s the ones I left out:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://xkcd.com/1988/&quot; target=&quot;_blank&quot;&gt;https://xkcd.com/1988/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://xkcd.com/927/&quot; target=&quot;_blank&quot;&gt;https://xkcd.com/927/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Further reading: &lt;a href=&quot;https://drewdevault.com/2021/09/27/Let-distros-do-their-job.html&quot; target=&quot;_blank&quot;&gt;Developers: Let distros do their job&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Python-stop-screwing-distros-over/</link>
        
        <pubDate>Tue, 16 Nov 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Python-stop-screwing-distros-over/</guid>
      </item>
    
      <item>
        
        
          <title>I will pay you cash to delete your npm module</title>
          <description>
            &lt;p&gt;npm’s culture presents a major problem for global software security. It’s grossly irresponsible to let dependency trees grow to thousands of dependencies, from vendors you may have never heard of and likely have not critically evaluated, to solve trivial tasks which could have been done from scratch in mere seconds, or, if properly considered, might not even be needed in the first place.&lt;/p&gt;&lt;p&gt;We need to figure out a way to curb this reckless behavior, but how?&lt;/p&gt;&lt;p&gt;I have an idea. Remember left-pad? That needs to happen more often.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/e2f3d3a4.svg&quot;&gt;
&lt;figcaption&gt;A LaTeX rendering of an equation which sets a reward (in dollars) to the logarithm of weekly downloads over lines of code in base 10 times one hundred&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I’ll pay you cold hard cash to delete your npm module. The exact amount will be determined on this equation, which is designed to offer higher payouts for modules with more downloads and fewer lines of code. A condition of this is that you must delete it without notice, so that everyone who depends on it wakes up to a broken build.&lt;/p&gt;&lt;p&gt;Let’s consider an example: &lt;a href=&quot;https://www.npmjs.com/package/isarray&quot; target=&quot;_blank&quot;&gt;isArray&lt;/a&gt;. It has only four lines of code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;javascript&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;toString&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;isArray&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;[object Array]&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With 51 million downloads this week, this works out to a reward of $710.&lt;/p&gt;&lt;p&gt;To prevent abuse, we’ll have to agree to each case in advance. I’ll review your module to make sure it qualifies, and check for any funny business like suspicious download figures or minified code. We must come to an agreement &lt;em&gt;before&lt;/em&gt; you delete the module, since I will not be able to check the line counts or download numbers after it’s gone.&lt;/p&gt;&lt;p&gt;I may also ask you to wait to delete your module, so that the chaos from each deletion is separated by a few weeks to maximize the impact. Also, the reward is capped at $1,000, so that I can still pay rent after this.&lt;/p&gt;&lt;p&gt;Do we have a deal? &lt;a href=&quot;#conclusion&quot;&gt;Click here to apply →&lt;/a&gt;&lt;/p&gt;&lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt;
&lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt;
&lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt;
&lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt;
&lt;div id=&quot;conclusion&quot;&gt;&lt;p&gt;Alright, the gig is up: this is satire. I’m not actually going to pay you to delete your npm module, nor do I want to bring about a dark winter of chaos in the Node ecosystem. Plus, &lt;a href=&quot;https://blog.npmjs.org/post/141905368000/changes-to-npms-unpublish-policy&quot; target=&quot;_blank&quot;&gt;it wouldn’t actually work&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I do hope that this idea strikes fear in the hearts of any Node developers that read it, and in other programming language communities which have taken after npm. What are you going to do if one of your dependencies vanishes?  What if someone studies the minified code on your website, picks out an obscure dependency they find there, then bribes the maintainers?&lt;/p&gt;&lt;p&gt;Most Node developers have no idea what’s in their dependency tree. Most of them are thousands of entries long, and have never been audited. This behavior is totally reckless and needs to stop.&lt;/p&gt;&lt;p&gt;Most of my projects have fewer than 100 dependencies, and many have fewer than 10. Some have zero. This is by design. You can’t have a free lunch, I’m afraid. Adding a dependency is a serious decision which requires consensus within the team, an audit of the new dependency, an understanding of its health and long-term prospects, and an ongoing commitment to re-audit them and be prepared to change course as necessary.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;isArray license:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Copyright (c) 2013 Julian Gruber &amp;lt;julian@juliangruber.com&amp;gt;.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &amp;quot;Software&amp;quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Cash-for-leftpad/</link>
        
        <pubDate>Tue, 16 Nov 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Cash-for-leftpad/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, November 2021</title>
          <description>
            &lt;p&gt;Hello again! Following a spooky month, we find ourselves again considering the progress of our eternal march towards FOSS world domination.&lt;/p&gt;&lt;p&gt;I’ll first address SourceHut briefly: today is the third anniversary of the opening of the public alpha! I have written &lt;a href=&quot;https://sourcehut.org/blog/2021-11-15-sourcehuts-third-year/&quot; target=&quot;_blank&quot;&gt;a longer post for sourcehut.org&lt;/a&gt; which I encourage you to read for all of the details.&lt;/p&gt;&lt;p&gt;In other news, we have decided to delay the release of our new programming language, perhaps by as much as a year. We were aiming for February ‘22, but slow progress on some key areas such as cryptography and the self-hosting compiler, plus the looming necessity of the full-scale acceptance testing of the whole language and standard library, compound to make us unsure about meeting the original release plans. However, progress is slow but moving. We have incorporated the first parts of AES support in our cryptography library, and ported the language to FreeBSD. A good start on date/time support has been under development and I’m pretty optimistic about the API design we’ve come up with. Things are looking good, but it will take longer than expected.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/visurf&quot; target=&quot;_blank&quot;&gt;visurf&lt;/a&gt; has enjoyed quite a bit of progress this month, thanks in large part to the help of a few new contributors. Nice work, everyone! We could still use more help, so please swing by the #netsurf channel on Libera Chat if you’re interested in participating. Improvements which landed this month include configuration options, url filtering via &lt;a href=&quot;https://git.sr.ht/~sircmpwn/dotfiles/tree/master/bin/urlfilter&quot; target=&quot;_blank&quot;&gt;awk scripts&lt;/a&gt;, searching through pages, and copying links on the page with the link following tool.&lt;/p&gt;&lt;p&gt;Projects which received minor updates this month include scdoc, gmni, kineto, and godocs.io. That’s it for today! My focus for the next month will be much the same as this month: SourceHut GraphQL work and programming language work. See you in another month!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-November-2021/</link>
        
        <pubDate>Mon, 15 Nov 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-November-2021/</guid>
      </item>
    
      <item>
        
        
          <title>Breaking down Apollo Federation&apos;s anti-FOSS corporate gaslighting</title>
          <description>
            &lt;p&gt;Gather around, my friends, for there is another company which thinks we are stupid and we enjoy having our faces spat in. &lt;a href=&quot;https://www.apollographql.com/blog/announcement/moving-apollo-federation-2-to-the-elastic-license-v2/&quot; target=&quot;_blank&quot;&gt;Apollo Federation&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; has announced that they will switch to a non-free license. Let’s find out just how much the Elastic license really is going to “protect the community” like they want you to believe.&lt;/p&gt;&lt;p&gt;Let’s start by asking ourselves, objectively, what practical changes can we expect from a switch from the &lt;a href=&quot;https://mit-license.org&quot; target=&quot;_blank&quot;&gt;MIT license&lt;/a&gt; to the &lt;a href=&quot;https://www.elastic.co/licensing/elastic-license&quot; target=&quot;_blank&quot;&gt;Elastic License&lt;/a&gt;? Both licenses are pretty short, so I recommend quickly reading them yourself before we move on.&lt;/p&gt;&lt;p&gt;I’ll summarize the difference between these licenses. First, the Elastic license offers you (the recipient of the software) one benefit that MIT does not: an explicit license for any applicable patents. However, it also has many additional restrictions, such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;No sublicensing (e.g. incorporating part of it into your own program)&lt;/li&gt;&lt;li&gt;No resale (e.g. incorporating it into Red Hat and selling support)&lt;/li&gt;&lt;li&gt;No modifications which circumvent the license key activation code&lt;/li&gt;&lt;li&gt;No use in a hosted or managed service&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is an objective analysis of the change. How does Apollo explain the changes?&lt;/p&gt;&lt;blockquote&gt;&lt;h3&gt;Why the new license?&lt;/h3&gt;&lt;p&gt;The Apollo developer community is at the heart of everything we do. As stewards of our community, we have a responsibility to prevent harm from anyone who intends to exploit our work without contributing back. We want to continue serving you by funding the development of important open-source graph technology for years to come. To honor that commitment, we’re moving Apollo Federation 2 to the Elastic License v2 (ELv2).&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Taking them at their word, this change was motivated by their deep care for their developer community. They want to “honor their commitment”, which is to “fund the development of important open-source graph technology” and “prevent harm from anyone who intends to exploit our work without contributing back”.&lt;/p&gt;&lt;p&gt;This is a very misleading statement. The answer to the question stated by the header is “funding the development”, but they want us to first think that they’re keeping the community at the heart of this decision — a community that they have just withheld several rights from. Their wording also seeks to link the community with the work, “our work”, when the change is clearly motivated from a position where Apollo believes they have effective ownership over the software, sole right to its commercialization, and a right to charge the community a rent — enforced via un-circumventable license key activation code. The new license gives Apollo exclusive right to commercial exploitation of the software — so they can “exploit our work”, but the community itself cannot.&lt;/p&gt;&lt;p&gt;What’s more, the change does not fund “open-source graph technology” as advertised, because after this change, Apollo Federation is no longer open source. The term “open source” is defined by the &lt;a href=&quot;https://opensource.org/osd&quot; target=&quot;_blank&quot;&gt;Open Source Definition&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, whose first clause is:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[The distribution terms of open-source software] shall not restrict any party from selling or giving away the software as a component of an aggregate software distribution containing programs from several different sources. The license shall not require a royalty or other fee for such sale.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The OSD elaborates later:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The license must not restrict anyone from making use of the program in a specific field of endeavor. For example, it may not restrict the program from being used in a business, or from being used for genetic research.&lt;/p&gt;&lt;p&gt;The rights attached to the program must apply to all to whom the program is redistributed without the need for execution of an additional license by those parties.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The Elastic license clearly does not meet this criteria.&lt;/p&gt;&lt;p&gt;Reading the Apollo announcement further, it continues to peddle this and other lies. The next paragraph attempts to build legitimacy for its peers in this anti-FOSS gaslighting movement:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Open-source licensing is evolving with the cloud. Many successful companies built on open-source technology (such as Elastic, MongoDB, and Confluent) have followed the path we’re taking to protect their communities and combine open, collaborative development with the benefits of cloud services that are easy to adopt and manage.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;They continue to use “open-source” language throughout, and misleads us into believing that they’ve made this change to protect the community and empower developers.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;When the Elastic License v2 was released, Elastic CEO Shay Banon called upon open-source companies facing a similar decision to “coalesce around a smaller number of licenses.” We’re excited to be part of this coalition of modern infrastructure companies building businesses that empower developers. […] Moving the Apollo Federation libraries and gateway to ELv2 helps us focus on our mission: empowering all of you.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It should be evident by now that this is complete horseshit. Let me peel away the bullshit and explain what is actually going on here in plain English.&lt;/p&gt;&lt;p&gt;Free and open source software can be commercialized — this is an essential requirement of the philosophy! However, it cannot be &lt;em&gt;exclusively&lt;/em&gt; commercialized. Businesses which participate in the FOSS ecosystem must give up their intellectual property monopoly, and allow the commercial ecosystem to flourish within their community — not just within their own ledger. They have to make their hosted version &lt;em&gt;better&lt;/em&gt; than the competitors, or seek other monetization strategies: selling books, support contracts, consulting, early access to security patches, and so on.&lt;/p&gt;&lt;p&gt;The community, allegedly at the heart of everything Apollo does, participates in the software’s development, marketing, and growth, and they are rewarded with the right to commercialize it. The community is incentivized to contribute back because they retain their copyright and the right to monetize the software. &lt;a href=&quot;https://github.com/apollographql/apollo-client/graphs/contributors&quot; target=&quot;_blank&quot;&gt;634 people&lt;/a&gt; have contributed to Apollo, and the product is the sum of their efforts, and should belong to them — not just to the business which shares a name with the software. The community built their projects on top of Apollo based on the open source social contract, and gave their time, effort, and copyright for their contributions to it, and Apollo pulled the rug out from under them. In the words of Bryan Cantrill, this shameful, reprehensible behavior is &lt;a href=&quot;https://invidious.mnus.de/watch?v=-zRN7XLCRhc&amp;t=2483&quot; target=&quot;_blank&quot;&gt;shitting in the pool of open source&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The smashing success of the free and open source software movement, both socially and commercially, has attracted the attention of bad actors like Apollo, who want to capitalize on this success without meeting its obligations. This wave of nonfree commercial gaslighting is part of a pattern where a company builds an open-source product, leverages the open-source community to build a market for it and to &lt;em&gt;directly&lt;/em&gt; improve the product via their contributions, then switches to a nonfree license and steals the work for themselves, fucking everyone else over.&lt;/p&gt;&lt;p&gt;Fuck Matt DeBergalis, Shay Banon, Jay Kreps, and Dev Ittycheria. These are the CEOs and CTOs responsible for this exploitative movement. They are morally bankrupt assholes and rent-seekers who gaslight and exploit the open source community for personal gain.&lt;/p&gt;&lt;p&gt;This is a good reminder that this is the ultimate fate planned by any project which demands a copyright assignment from contributors in the form of a Contributor License Agreement (CLA). &lt;a href=&quot;https://drewdevault.com/2018/10/05/Dont-sign-a-CLA.html&quot; target=&quot;_blank&quot;&gt;Do not sign these&lt;/a&gt;! Retain your copyright over your contributions and contribute to projects which are collectively owned by their community — because &lt;em&gt;that’s&lt;/em&gt; how you honor your community.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Previously:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/01/19/Elasticsearch-does-not-belong-to-Elastic.html&quot; target=&quot;_blank&quot;&gt;Elasticsearch does not belong to Elastic&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/01/20/FOSS-is-to-surrender-your-monopoly.html&quot; target=&quot;_blank&quot;&gt;Open source means surrendering your monopoly over commercial exploitation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/04/12/DCO.html&quot; target=&quot;_blank&quot;&gt;The Developer Certificate of Origin is a great alternative to a CLA&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you are an Apollo Federation user who is affected by this change, I have set up &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/apollo-fork&quot; target=&quot;_blank&quot;&gt;a mailing list&lt;/a&gt; to organize a community-maintained fork. Please send an email to this list if you are interested in participating in such a fork.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Apollo-federation-2-gaslighting/</link>
        
        <pubDate>Fri, 05 Nov 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Apollo-federation-2-gaslighting/</guid>
      </item>
    
      <item>
        
        
          <title>GitHub stale bot considered harmful</title>
          <description>
            &lt;p&gt;Disclaimer: I work for a GitHub competitor.&lt;/p&gt;&lt;p&gt;One of GitHub’s “recommended” marketplace features is the “stale” bot. The purpose of this bot is to automatically close GitHub issues after a period of inactivity, 60 days by default. You have probably encountered it yourself in the course of your work.&lt;/p&gt;&lt;p&gt;This is a terrible, horrible, no good, very bad idea.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/e2f0d39c.png&quot;&gt;
&lt;figcaption&gt;A screenshot of an interaction with this bot&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I’m not sure what motivates maintainers to install this on their repository, other than the fact that GitHub recommends it to them. Perhaps it’s motivated by a feeling of shame for having a lot of unanswered issues? If so, this might stem from a misunderstanding of the responsibilities a maintainer has to their project. You are not obligated to respond to every issue, implement every feature request, or fix every bug, or even acknowledge them in any way.&lt;/p&gt;&lt;p&gt;Let me offer you a different way of thinking about issues: a place for motivated users to collaborate on narrowing down the problem and planning a potential fix. A space for the community to work, rather than an action item for you to deal with personally. It gives people a place to record additional information, and, ultimately, put together a pull request for you to review. It does not matter if this process takes days or weeks or years to complete. Over time, the issue will accumulate details and workarounds to help users identify and diagnose the problem, and to provide information for the person that might eventually write a patch/pull request.&lt;/p&gt;&lt;p&gt;It’s entirely valid to just ignore your bug tracker entirely and leave it up to users to deal with themselves. There is no shame in having a lot of open issues — if anything, it signals popularity. Don’t deny your users access to an important mutual support resource, and a crucial funnel to bring new contributors into your project.&lt;/p&gt;&lt;p&gt;This is the approach I would recommend on GitHub, but for illustrative purposes I’ll also explain a slightly modified approach I encourage for SourceHut users. sr.ht provides mailing lists (and, soon, IRC chat rooms), which are recommended for first-line support and discussion about your project, including bug reports, troubleshooting, and feature requests, instead of filing a ticket (our name for issues). The mailing list gives you a space to refine the bug report, solicit extra details or point out an existing ticket, or clarifying and narrowing down feature requests. This significantly improves the quality of bug reports, eliminates duplicates, and better leverages the community for support, resulting in every single ticket representing a unique, actionable item.&lt;/p&gt;&lt;p&gt;I will eventually ask the user to file a ticket when the bug or feature request is confirmed. This does not imply that I will follow up with a fix or implementation on any particular time frame. It just provides this space I discussed before: somewhere to collect more details, workarounds, and additional information for users who experience a bug or want a feature, and to plan for its eventual implementation at an undefined point in the future, either from a SourceHut maintainer or from the community.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/stalebot/</link>
        
        <pubDate>Tue, 26 Oct 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/stalebot/</guid>
      </item>
    
      <item>
        
        
          <title>How SmarterEveryDay&apos;s 4privacy can, and cannot, meet its goals</title>
          <description>
            &lt;p&gt;I don’t particularly find myself to be a fan of the SmarterEveryDay YouTube channel, simply for being outside of Destin’s target audience most of the time. I understand that Destin, the channel’s host, is a friendly person and a great asset to his peers, and that he generally strives to do good. When I saw that he was involved in a Kickstarter to develop a privacy product, it piqued my interest. As a privacy advocate and jaded software engineer, I set out to find out what it’s all about.&lt;/p&gt;&lt;p&gt;&lt;em&gt;You can watch the YouTube video &lt;a href=&quot;https://www.youtube.com/watch?v=KMtrY6lbjcY&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;, and a short follow-up &lt;a href=&quot;https://www.youtube.com/watch?v=Hy6STq337qo&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;There are several things to praise here. I honestly thought that Destin’s coverage of the topic of privacy for the layman was really well presented, and took some notes to use the next time I’m explaining privacy issues to my friends. The coverage of the history of wiretapping and the pivotal role played by 9/11, complete with an empathetic view of the mindset of American adults contemporary to it that many find hard to express, along with great drone shots of Big Tech’s mysterious datacenters, this is all great stuff. For the right project, Destin is a valuable asset with a large audience and a lot of experience in making complex issues digestible for the every-person, and 4privacy is lucky to have access to him.&lt;/p&gt;&lt;p&gt;A lot of the buzzwords and things found on their &lt;a href=&quot;https://4privacy.com/our-technology/&quot; target=&quot;_blank&quot;&gt;technology page&lt;/a&gt; are promising as well. The focus on end-to-end encryption and zero-knowledge principles, &lt;em&gt;and&lt;/em&gt; the commitment to open source, are absolutely necessary and are great to see here. A lot of the tech described, although briefly, seems like it’s on the right track. The ability to use your own service provider, and the focus on decentralization and federation, is very good.&lt;/p&gt;&lt;p&gt;I do have some concerns, however. Let’s break them down into these categories:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Incentives and economics&lt;/li&gt;&lt;li&gt;Responsibilities and cultivating trust&lt;/li&gt;&lt;li&gt;Ambitions and feasibility&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Given the value ($$$) associated with private user information, it’s important to know that the trove of private information overseen by a company like this is safe from threats from the robber-barons of tech. 4privacy is &lt;a href=&quot;https://4privacy.com/contact-us/&quot; target=&quot;_blank&quot;&gt;looking for investors&lt;/a&gt;, which is a red flag: investors demand a return, and if the product isn’t profitable, user data is the first thing up for auction. So, how will 4privacy make money? We need to know. They might say that the E2EE prevents them from directly monetizing user data, and they’re right, but that’s only for today. If they become a market incumbent, they will have the power to change the technology in a way which compromises privacy faster than we can move to another system, and we need to understand that this will not happen.&lt;/p&gt;&lt;p&gt;Growing consumer awareness in privacy issues over the past decade, combined with a generally low level of technology literacy in the population, has allowed a lot of grifters to arise. One of the common forms these grifts take is seen in the rise of VPN companies, which prey on consumer fear and often use YouTube as a marketing channel, &lt;a href=&quot;https://www.youtube.com/watch?v=OdPoVi_h0r0&quot; target=&quot;_blank&quot;&gt;including on Destin’s previous videos&lt;/a&gt;. Another giant, flaming red flag appears whenever cryptocurrency is involved. In general terms, the privacy space is thoroughly infested with bad actors, which makes matters of trust very difficult. 4privacy needs to be prepared to be very honest and transparent with not only their tech, but their financial structure and incentives. With SourceHut, I had to &lt;em&gt;engineer&lt;/em&gt; our incentives to suit stated goals, and I communicate this to users so that they can make informed choices about us. 4privacy would be wise to take similar steps, in full view of the public.&lt;/p&gt;&lt;p&gt;Empowering users to make informed choices leads me into our next point: is 4privacy ready to bear the burden of responsibility for this system? As far as I can glean from their mock-ups, they plan to be handling your government IDs, passwords, healthcare information, confidential attorney/client communications, and so on.  The consequences of having this information compromised are grave, and this demands world-class security. It’s also extremely important for 4privacy to be honest with their users about what their security model can, and cannot, make promises about.&lt;/p&gt;&lt;p&gt;You must be honest with your users, and help them to understand how the system works, and when it doesn’t work, so that they can make informed choices about how to trust it. This can be difficult when the profit motive is involved, because they might conclude that they &lt;em&gt;don’t&lt;/em&gt; want to use your service. It’s even more difficult when you exist in a space full of grifters that are happy to tell sweet lies to your users about fixing all of their problems. However, it must be done.&lt;/p&gt;&lt;p&gt;Privacy tools are relied upon by vulnerable people facing challenging situations. If you promise something you cannot deliver on, and they depend on you to keep their information private in impossible conditions, when the other shoe drops there could be dramatic consequences for their lives. If a journalist in a war-torn country depends on you to keep their documents private, and you fail, they could end up in prison or a labor camp or splattered on the wall of a dark alley, and it’ll have been your fault. You &lt;em&gt;must&lt;/em&gt; be forthright and realistic with users about how your system can and cannot keep them safe. I hope Destin’s future videos in the privacy series will cover how the system works in more detail, including its limitations. He is skilled at explaining complicated topics in a comprehensible manner for everyday people to understand, and I hope he will leverage these skills here.&lt;/p&gt;&lt;p&gt;I have already noticed one place where they have failed to be honest in their limitations, however, and it presents a major concern for me. Much of their marketing speaks of the ability to &lt;em&gt;revoke&lt;/em&gt; access to your private information &lt;em&gt;after&lt;/em&gt; a third-party has been provided access to it. This is, frankly, entirely impossible, and I think it is extraordinarily irresponsible to design your application in a manner that suggests that it can be done. To keep things short, I’ll refute the idea as briefly as possible: what’s to stop someone from taking a picture of the phone while it’s displaying your private info? Or writing it down? When you press the “revoke” button in the app, and it dutifully disappears from their phone screen,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; the private information is still written on a piece of paper in their desk drawer and you’re none the wiser. The application has given you a &lt;em&gt;false sense of security&lt;/em&gt;, which is a major problem for a privacy-oriented tool.&lt;/p&gt;&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; work in this problem space, albeit under severely limited constraints. For example, consider how the SSH agent works: an application which wants to use your private keys to sign something can ask the agent for help, but the agent will not provide the cryptographic keys for it to use directly — the agent will do the cryptographic operation on the application’s &lt;em&gt;behalf&lt;/em&gt; and send the &lt;em&gt;results&lt;/em&gt; to the application to use. These constraints limit the use-cases significantly, such that, for example, you could not send someone your social security number using this system. You could, however, design a protocol in which an organization which needs to verify your identity can ask, in programmatic terms, “is this person who they say they are?”, and 4privacy answers, possibly consulting their SSN, “yes” or “no”. This does not seem to be what they’re aiming for, however.&lt;/p&gt;&lt;p&gt;So, with all of this in mind, how ambitious is their idea as a whole? Is it feasible? What kind of resources will they need to pull it off?&lt;/p&gt;&lt;p&gt;In short, this idea is extraordinarily ambitious. They are designing a novel cryptosystem, which is an immediate red flag: designing a secure cryptosystem is one of the most technologically challenging feats a programming team can undertake. Furthermore, they’re building a distributed, federated system, which is itself a highly complex and challenging task, even more so when the system is leveraged to exchange sensitive information. It can be done, but it takes an extraordinarily talented team with hard-core technical chops and a lot of experience.&lt;/p&gt;&lt;p&gt;What’s more, if they were to do this well, it would involve developing and standardizing open protocols. This requires a greater degree of openness and community participation than &lt;a href=&quot;https://github.com/4PrivacyEngine/4PrivacyEngine-Core&quot; target=&quot;_blank&quot;&gt;they are planning to do&lt;/a&gt;. Furthermore, they need to get others to agree to implement these protocols, which involves solving social and political problems — both in technical and non-technical senses. For instance, the Dutch government stores much of my personal information in the DigiD system. Will they be able to convince the Netherlands to work with their protocols? How about every other country? And, if they want me to store my health insurance in the app, how are they going to convince my doctor to use the app to receive it? And how about every other doctor? And what about all of the other domains they want to be involved in outside of healthcare data? Will they interoperate with legacy systems to achieve the market penetration they need? Will those legacy systems provide for their end-to-end encryption needs, and if not, will users understand the consequences?&lt;/p&gt;&lt;p&gt;I’m not saying that any of this is impossible — only that it is extraordinarily difficult to pull off. Extraordinary projects require extraordinary resources.  They will need multiple highly talented engineering teams working in parallel, and the support staff necessary to keep them going.&lt;/p&gt;&lt;p&gt;Their goal on Kickstarter, which was quickly met and exceeded, is $175,000. This is nowhere near enough, so either they aren’t going to pull it off, or they have more money from somewhere else. Destin is acknowledged as an investor, and they are seeking more investments on their website — how much money, and from whom, now and in the future? By taking the lion’s share from entities other than their users, they have set up concerning incentives in which the entities responsible for private data have millions on the line and are itchy to get returns, and the entities whom the private data concerns haven’t been invited to the negotiating table.&lt;/p&gt;&lt;p&gt;In short, I would urge them to do the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Make clear their funding sources, incentive model, and plans for monetization. Tell everyone the pitch they tell to private investors.&lt;/li&gt;&lt;li&gt;Publish their whitepaper draft and invite public comment now, rather than when it’s “finished”. Consider doing the same with the source code.&lt;/li&gt;&lt;li&gt;Work to inform potential users about how the technology works, to the extent that they can make informed choices about it. Destin would be a great help for this.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;4privacy should generally institute a policy of greater transparency and openness by default, preferring to keep private only what they absolutely must. There is no shame in iterating on an incomplete product in the view of the public. On the contrary, I am quite proud that my business works in this manner.&lt;/p&gt;&lt;p&gt;The fundraising campaign quickly met its goal and will presumably only continue to grow in the coming weeks — it’s reasonably certain that it will close with at least $1M raised. Having met their goal, the product will presumably ship, and we’ll see the answers to these questions eventually. The team has a lot of work ahead of them: good luck.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Smarter-every-day-and-4privacy/</link>
        
        <pubDate>Fri, 22 Oct 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Smarter-every-day-and-4privacy/</guid>
      </item>
    
      <item>
        
        
          <title>Software developers have stopped caring about reliability</title>
          <description>
            &lt;p&gt;Of all the principles of software engineering which has fallen by the wayside in the modern “move fast and break things” mentality of &lt;del&gt;assholes&lt;/del&gt; modern software developers, reliability is perhaps the most neglected, along with its cousin, robustness. Almost all software that users encounter in $CURRENTYEAR is straight-up broken, and often badly.&lt;/p&gt;&lt;p&gt;Honestly, it’s pretty embarassing. Consider all of the stupid little things you’ve learned how to do in order to work around broken software. Often something as simple as refreshing the page or rebooting the program to knock some sense back into it — most users can handle that. There are much stupider problems, however, and they are &lt;em&gt;everywhere&lt;/em&gt;. Every morning, I boot, then immediately hard-reboot, my workstation, because it seems to jigger my monitors into waking up properly to do their job. On many occasions, I have used the browser dev tools to inspect a broken web page to figure out how to make it do the thing I want to do, usually something complicated like submitting a form properly (a solved problem since 1993).&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;When the average person (i.e. a non-nerd) says they “don’t get computers”, I believe them. It’s not because they’re too lazy to learn, or because they’re backwards and outdated, or can’t keep with the times. It’s because computers are hard to understand. They are enigmatic and unreliable. &lt;strong&gt;I&lt;/strong&gt; know that when my phone suddenly stops delivering SMS messages mid-conversation, it’s not because I’ve been abandoned by my friend, but because I need to toggle airplane mode to reboot the modem. &lt;strong&gt;I&lt;/strong&gt; know that when I middle click a link and “javascript:;” opens in a new tab, &lt;del&gt;an asshole&lt;/del&gt; a developer wants me to left click it instead. Most people don’t understand this! You and I, dear reader, have built up an incredible amount of institutional knowledge about how to deal with broken computers. We’ve effectively had to reverse engineer half the software we’ve encountered to figure out just where to prod it to make it do the thing you asked. If you don’t have this background, then computers are a nightmare.&lt;/p&gt;&lt;p&gt;It’s hard to overstate just how much software developers have given the finger to reliability in the past 10 years or so. It’s for the simplest, silliest reasons, too, like those web forms. My web browser has been perfectly competent at submitting HTML forms for the past 28 years, but for some stupid reason some &lt;del&gt;asshole&lt;/del&gt; developer decided to reimplement all of the form semantics in JavaScript, and now I can’t pay my electricity bill without opening up the dev tools. Imagine what it’s like to not know how to do that. Imagine if you were blind.&lt;/p&gt;&lt;p&gt;Folks, this is not okay. Our industry is characterized by institutional recklessness and a callous lack of empathy for our users. It’s time for a come-to-jesus moment. This is our fault, and yes, dear reader, you are included in that statement. We are personally responsible for this disaster, and we must do our part to correct it.&lt;/p&gt;&lt;p&gt;This is what you must do.&lt;/p&gt;&lt;p&gt;You must prioritize simplicity. You and I are not smart enough to be clever, so don’t try. As the old saying goes, there are two kinds of programs: those simple enough to obviously have no bugs, and those complicated enough to have no obvious bugs. It is by no means easier to make the simpler kind, in fact, it’s much more difficult. However, the simpler the system is, the easier it is to reason about all of its states and edge cases. You do not need a JavaScript-powered custom textbox widget. YOU DO NOT NEED A JAVASCRIPT-POWERED CUSTOM TEXTBOX WIDGET.&lt;/p&gt;&lt;p&gt;On the subject of state, state is the language of robustness. When something breaks, it’s because a state occured that you didn’t plan for. Think about your program in terms of this state. Design data structures that cannot represent invalid states (within reason), and then enumerate each of those possible states and &lt;em&gt;check&lt;/em&gt; that your application does something reasonable in that situation.&lt;/p&gt;&lt;p&gt;Identify your error cases, plan for them, implement that plan, and then &lt;em&gt;test it&lt;/em&gt;. Sometimes things don’t work! Most languages give you tools to identify error cases and handle them appropriately, so use them. And again, for the love of god, &lt;em&gt;test it&lt;/em&gt;. If you commit and push a line of code that you have not personally watched run and work as expected, you have failed to do your job properly.&lt;/p&gt;&lt;p&gt;Prefer to use proven technologies. If you use unproven technologies, you must use them scarcely, and you must &lt;em&gt;personally&lt;/em&gt; understand them at an intimate level. If you haven’t read the source code for the brand-new database engine you heard about on HN two weeks ago, you shouldn’t be putting it into production.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Finally, stop letting economics decide everything you do. Yes, developers have finite time, and that time costs. Yes, users with annoying needs like accessibility and internationalization are more expensive to support than the returns they produce. &lt;em&gt;You need to pay for it anyway&lt;/em&gt;. It’s the right thing to do. We can be profitable &lt;em&gt;and&lt;/em&gt; empathetic. Don’t think about rushing to market first, and instead prioritize getting a &lt;em&gt;good product&lt;/em&gt; into your user’s hands. Our users are not cattle. It is not our job to convert attention into money at their expense. We need to treat users with respect, and that means testing our goddamn code before we ship it.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Do an exercise with me. Grab a notepad and make a note every time you encounter some software bug in production (be it yours or someone else’s), or need to rely on your knowledge as a computer expert to get a non-expert system to work. &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Email me&lt;/a&gt; your list in a week.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Reliability/</link>
        
        <pubDate>Sun, 17 Oct 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Reliability/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, October 2021</title>
          <description>
            &lt;p&gt;On this dreary morning here in Amsterdam, I’ve made my cup of coffee and snuggled my cat, and so I’m pleased to share some FOSS news with you. Some cool news today! We’re preparing for a new core product launch at sr.ht, cool updates for our secret programming language, plus news for visurf.&lt;/p&gt;&lt;p&gt;Simon Ser has been hard at work on expanding his &lt;a href=&quot;https://sr.ht/~emersion/soju/&quot; target=&quot;_blank&quot;&gt;soju&lt;/a&gt; and &lt;a href=&quot;https://sr.ht/~emersion/gamja/&quot; target=&quot;_blank&quot;&gt;gamja&lt;/a&gt; projects for the purpose of creating a new core sourcehut product: chat.sr.ht. We’re rolling this out in a private beta at first, to seek a fuller understanding of the system’s performance characteristics, to make sure everything is well-tested and reliable, and to make plans for scaling, maintenance, and general availability. In short, chat.sr.ht is a hosted IRC bouncer which is being made available to all paid sr.ht users, and a kind of webchat gateway which will be offered to unpaid and anonymous users. I’m pretty excited about it, and looking forward to posting a more detailed announcement in a couple of weeks. In other sourcehut news, work on GraphQL continues, with paste.sr.ht landing and todo.sr.ht’s writable API in progress.&lt;/p&gt;&lt;p&gt;Our programming langauge project grew some interesting features this month as well, the most notable of which is probably reflection. I wrote &lt;a href=&quot;https://drewdevault.com/2021/10/05/Reflection.html&quot; target=&quot;_blank&quot;&gt;an earlier blog post&lt;/a&gt; which goes over this in some detail. There’s also ongoing work to develop the standard library’s time and date support, riscv64 support is essentially done, and we’ve overhauled the grammar for switch and match statements to reduce a level of indentation for typical code. In the coming weeks, I hope to see date/time support and reflection fleshed out much more, and to see some more development on the self-hosted compiler.&lt;/p&gt;&lt;p&gt;Work has also continued apace on &lt;a href=&quot;https://sr.ht/~sircmpwn/visurf&quot; target=&quot;_blank&quot;&gt;visurf&lt;/a&gt;, which is a project I would love to have your help with — drop me a note on #netsurf on libera.chat if you’re interested. Since we last spoke, visurf has gained support for readline-esque keybindings on the exline, a “follow” mode for keyboard navigation, Wayland clipboard support, and a few other features besides. Please help! This project will need a lot of work to complete, and much of that work is very accessible to programmers of any skill level.&lt;/p&gt;&lt;p&gt;Also on the subject of Netsurf and Netsurf-adjacent work, I broke ground on &lt;a href=&quot;https://git.sr.ht/~sircmpwn/antiweb&quot; target=&quot;_blank&quot;&gt;antiweb&lt;/a&gt; this month. The goal of this project is to provide a conservative CSS toolkit which allows you to build web interfaces which are compatible with marginalized browsers like Netsurf and Lynx. I should be able to migrate my blog to this framework in the foreseeable future, and ultimately the sourcehut frontend will be overhauled with this framework.&lt;/p&gt;&lt;p&gt;And a collection of minor updates:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I have been working on Alpine Linux for RISC-V again, and have upstreamed the necessary patches to get u-Boot to bootstrap UEFI into grub for a reasonably sane boot experience. Next up will be getting this installed onto the onboard SPI flash so that it works more like a native firmware.&lt;/li&gt;&lt;li&gt;I have tagged versions 1.0 of &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gmnisrv&quot; target=&quot;_blank&quot;&gt;gmnisrv&lt;/a&gt; and &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gmni&quot; target=&quot;_blank&quot;&gt;gmni&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Adnan Maolood has been hard at work on &lt;a href=&quot;https://godocs.io&quot; target=&quot;_blank&quot;&gt;godocs.io&lt;/a&gt; and we should soon expect a 1.0 of our gddo fork as well, which should make it more or less plug-and-play to get a working godocs instance on localhost from your local Go module cache.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That’s all for today! Take care, and thank you as always for your continued support. I’ll see you next month!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-October-2021/</link>
        
        <pubDate>Fri, 15 Oct 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-October-2021/</guid>
      </item>
    
      <item>
        
        
          <title>How reflection works in Hare</title>
          <description>
            &lt;p&gt;&lt;em&gt;Note: this is a redacted copy of a blog post published on the internal development blog of a new systems programming language. The name of the project and further details are deliberately being kept in confidence until the initial release. You may be able to find it if you look hard enough — you have my thanks in advance for keeping it to yourself. For more information, see “&lt;a href=&quot;https://drewdevault.com/2021/03/19/A-new-systems-language.html&quot; target=&quot;_blank&quot;&gt;We are building a new systems programming language&lt;/a&gt;”.&lt;/em&gt;&lt;/p&gt;&lt;style&gt;
.redacted {
  background: black;
  foreground: black;
}
&lt;/style&gt;
&lt;p&gt;I’ve just merged support for reflection in Hare. Here’s how it works!&lt;/p&gt;&lt;h2&gt;Background&lt;/h2&gt;&lt;p&gt;“Reflection” refers to the ability for a program to examine the type system of its programming language, and to dynamically manipulate types and their values at runtime. You can learn more at &lt;a href=&quot;https://en.wikipedia.org/wiki/Reflective_programming&quot; target=&quot;_blank&quot;&gt;Wikipedia&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Reflection from a user perspective&lt;/h2&gt;&lt;p&gt;Let’s start with a small sample program:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;my_type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;typeinfo&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;my_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;int&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;id: {}&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;size: {}&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;alignment: {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;constant variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running this program produces the following output:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;int
id: 1099590421
size: 4
alignment: 4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This gives us a simple starting point to look at. We can see that “type” is used as the type of the “my_type” variable, and initialized with a “type(int)” expression. This expression returns a type value for the type given in the parenthesis — in this case, for the “int” type.&lt;/p&gt;&lt;p&gt;To learn anything useful, we have to convert this to a “types::typeinfo” pointer, which we do via &lt;code&gt;types::reflect&lt;/code&gt;. The typeinfo structure looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;typeinfo&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ID field is the type’s unique identifier, which is universally unique and deterministic, and forms part of Hare’s ABI. This is derived from an FNV-32 hash of the type information. You can find the ID for any type by modifying our little example program, or you can use the helper program in the &lt;code&gt;cmd/hare/type&lt;/code&gt; directory of the Hare source tree.&lt;/p&gt;&lt;p&gt;Another important field is the “repr” field, which is short for “representation”, and it gives details about the inner structure of the type. The repr type is defined as a tagged union of all possible type representations in the Hare type system:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;repr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;builtin&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;enumerated&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;pointer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;slice&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;struct_union&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;tagged&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the case of the “int” type, the representation is “builtin”:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;builtin&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;F32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;F64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;I16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;I64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;I8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RUNE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SIZE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;STR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;U16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;U32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;U64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;U8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;UINT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;UINTPTR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;VOID&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;TYPE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;builtin::INT&lt;/code&gt;, in this case. The structure and representation of the “int” type is defined by the Hare specification and cannot be overridden by the program, so no further information is necessary. The relevant part of the spec is:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/9fb3b7e2.png&quot;&gt;
&lt;figcaption&gt;“The precision of ‘int’ and ‘uint’ are implementation-defined. ‘int’ shall be signed, and ‘uint’ shall be unsigned. Both types shall be at least 32-bits in precision. The precision in bits shall be a power of two.”&lt;/figcaption&gt;&lt;/figure&gt; &lt;a href=&quot;https://redacted.moe/f/f13236c9.png&quot;&gt;A table from the specification showing the precision ranges of each integer type&lt;/a&gt;&lt;/p&gt;&lt;p&gt;More information is provided for more complex types, such as structs.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;error keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;my_type&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;my_type&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;id: {}&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;size: {}&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;alignment: {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;st&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_operator&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;struct_union&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;struct_kind&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;STRUCT&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;type_&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;{}: offset {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output of this program is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;id: 2617358403
size: 8
alignment: 4
	x: offset 0
	y: offset 4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here the “repr” field provides the “types::struct_union” structure:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;struct_union&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;struct_kind&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;struct_field&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;struct_kind&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;STRUCT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;UNION&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;struct_field&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;type_&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Makes sense? Excellent. So how does it all work?&lt;/p&gt;&lt;h2&gt;Reflection internals&lt;/h2&gt;&lt;p&gt;Let me first draw the curtain back from the magic “types::reflect” function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Returns [[typeinfo]] for the provided type.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;typeinfo&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;typeinfo&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It simply casts the “type” value to a pointer, which is what it is. When the compiler sees an expression like &lt;code&gt;let x = type(int)&lt;/code&gt;, it statically allocates the typeinfo data structure into the program and returns a pointer to it, which is then wrapped up in the opaque “type” meta-type. The “reflect” function simply converts it to a useful pointer. Here’s the generated IR for this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;4 &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;alloc8&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 8
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;storel&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;builtin_int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A clever eye will note that we initialize the value to a pointer to “rt.builtin_int”, rather than allocating a typeinfo structure here and now. The runtime module provides static typeinfos for all built-in types, which look like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;error&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;builtin_int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;typeinfo&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;typeinfo&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1099590421&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;sz&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;al&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;repr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are an internal implementation detail, hence “@hidden”. But many types are not built-in, so the compiler is required to statically allocate a typeinfo structure:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;data $strdata.7 = section &amp;quot;.data.strdata.7&amp;quot; { b &amp;quot;x&amp;quot; }

data $strdata.8 = section &amp;quot;.data.strdata.8&amp;quot; { b &amp;quot;y&amp;quot; }

data $sldata.6 = section &amp;quot;.data.sldata.6&amp;quot; {
  l $strdata.7, l 1, l 1, l 0, l $rt.builtin_int,
  l $strdata.8, l 1, l 1, l 4, l $rt.builtin_int,
}

data $typeinfo.5 = section &amp;quot;.data.typeinfo.5&amp;quot; {
  w 2617358403, z 4,
  l 8,
  l 4,
  w 0, z 4,
  w 5555256, z 4,
  w 0, z 4,
  l $sldata.6, l 2, l 2,
}

export function section &amp;quot;.text.main&amp;quot; &amp;quot;ax&amp;quot; $main() {
@start.0
	%binding.4 =l alloc8 8
@body.1
	storel $typeinfo.5, %binding.4
@.2
	ret
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This has the unfortunate effect of re-generating all of these typeinfo structures every time someone uses &lt;code&gt;type(struct { x: int, y: int })&lt;/code&gt;. We still have one trick up our sleeve, though: type aliases! Most people don’t actually use anonymous structs like this often, preferring to use a type alias to give them a name like “coords”. When they do this, the situation improves:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;coords&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant field variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;data $strdata.1 = section &amp;quot;.data.strdata.1&amp;quot; { b &amp;quot;coords&amp;quot; }

data $sldata.0 = section &amp;quot;.data.sldata.0&amp;quot; { l $strdata.1, l 6, l 6 }

data $strdata.4 = section &amp;quot;.data.strdata.4&amp;quot; { b &amp;quot;x&amp;quot; }

data $strdata.5 = section &amp;quot;.data.strdata.5&amp;quot; { b &amp;quot;y&amp;quot; }

data $sldata.3 = section &amp;quot;.data.sldata.3&amp;quot; {
  l $strdata.4, l 1, l 1, l 0, l $rt.builtin_int,
  l $strdata.5, l 1, l 1, l 4, l $rt.builtin_int,
}

data $typeinfo.2 = section &amp;quot;.data.typeinfo.2&amp;quot; {
  w 2617358403, z 4,
  l 8,
  l 4,
  w 0, z 4,
  w 5555256, z 4,
  w 0, z 4,
  l $sldata.3, l 2, l 2,
}

data $type.1491593906 = section &amp;quot;.data.type.1491593906&amp;quot; {
  w 1491593906, z 4,
  l 8,
  l 4,
  w 0, z 4,
  w 3241765159, z 4,
  l $sldata.0, l 1, l 1,
  l $typeinfo.2
}

export function section &amp;quot;.text.main&amp;quot; &amp;quot;ax&amp;quot; $main() {
@start.6
	%binding.10 =l alloc8 8
@body.7
	storel $type.1491593906, %binding.10
@.8
	ret
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The declaration of a type alias provides us with the perfect opportunity to statically allocate a typeinfo singleton for it. Any of these which go unused by the program are automatically stripped out by the linker thanks to the &lt;code&gt;--gc-sections&lt;/code&gt; flag. Also note that a type alias is considered a distinct representation from the underlying struct type:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;secondary&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This explains the differences in the structure of the “type.1491593906” global. The &lt;code&gt;struct { x: int, y: int }&lt;/code&gt; type is the “secondary” field of this type.&lt;/p&gt;&lt;h2&gt;Future improvements&lt;/h2&gt;&lt;p&gt;This is just the first half of the equation. The next half is to provide useful functions to work with this data. One such example is “types::strenum”:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Returns the value of the enum at &amp;quot;val&amp;quot; as a string. Aborts if the value is&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// not present. Note that this does not work with enums being used as a flag&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// type, see [[strflag]] instead.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;strenum&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;en&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ty&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;repr&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;enumerated&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;I8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;U8&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;I16&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;U16&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;U32&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;I64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;U64&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;UINT&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SIZE&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;enum has invalid value&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is used like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;watchmen&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;VIMES&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;CARROT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ANGUA&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;COLON&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;NOBBY&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;officer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;watchmen&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ANGUA&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;strenum&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;watchmen&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;officer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// Prints ANGUA&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additional work is required to make more useful tools like this. We will probably want to introduce a “value” abstraction which can store an arbitrary value for an arbitrary type, and helper functions to assign to or read from those values. A particularly complex case is likely to be some kind of helper for calling a function pointer via reflection, which we I may cover in a later article. There will also be some work to bring the “types” (reflection) module closer to the hare::* namespace, which already features hare::ast, hare::parse, and hare::types, so that the parser, type checker, and reflection systems are interopable and work together to implement the Hare type system.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;Want to help us build this language? We are primarily looking for help in the following domains:&lt;/em&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;Architectures or operating systems, to help with ports&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;Compilers &amp; language design&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;Cryptography implementations&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;Date &amp; time implementations&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;Unix&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;em&gt;If you’re an expert in a domain which is not listed, but that you think we should know about, then feel free to reach out. Experts are perferred, motivated enthusiasts are acceptable. &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Send me an email&lt;/a&gt; if you want to help!&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Reflection/</link>
        
        <pubDate>Tue, 05 Oct 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Reflection/</guid>
      </item>
    
      <item>
        
        
          <title>Developers: Let distros do their job</title>
          <description>
            &lt;p&gt;I wrote a post some time ago titled &lt;a href=&quot;https://drewdevault.com/2019/12/09/Developers-shouldnt-distribute.html&quot; target=&quot;_blank&quot;&gt;Developers shouldn’t distribute their own software&lt;/a&gt;, and after a discussion on the sr.ht IRC channel today, the topic seems worthy of renewed mention. Let’s start with this: what exactly is a software distribution, anyway?&lt;/p&gt;&lt;p&gt;I use “software distribution” here, rather than “Linux distribution”, because it generalizes better. For example, all of the major BSD systems, plus Illumos and others besides, are software distributions, but don’t involve Linux. Some differ further still, sitting on top of another operating system, such as Nix or pkgsrc. What these systems all have in common is that they concern themselves with the &lt;em&gt;distribution&lt;/em&gt; of &lt;em&gt;software&lt;/em&gt;, and thus are a &lt;em&gt;software distribution&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;An important trait of these systems is that they function independently of the development of the software they distribute, and are overseen by a third party. For the purpose of this discussion, I will rule out package repositories which are not curated by the third-party in question, such as npm or PyPI. It is no coincidence that such repositories often end up &lt;a href=&quot;https://www.zdnet.com/article/two-malicious-python-libraries-removed-from-pypi/&quot; target=&quot;_blank&quot;&gt;distributing&lt;/a&gt; &lt;a href=&quot;https://www.trendmicro.com/vinfo/dk/security/news/cybercrime-and-digital-threats/hacker-infects-node-js-package-to-steal-from-bitcoin-wallets&quot; target=&quot;_blank&quot;&gt;malware&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Software distributions are often volunteer-run and represent the interests of the users; in a sense they are a kind of &lt;a href=&quot;https://en.wikipedia.org/wiki/Trade_union&quot; target=&quot;_blank&quot;&gt;union&lt;/a&gt; of users. They handle building your software for their system, and come to the table with domain-specific knowledge about the concerns of the platform that they’re working with. There are hundreds of Linux distros and each does things differently — the package maintainers are the experts who save you the burden of learning how all of them work. Instead of cramming all of your files into /opt, they will carefully sort it into the right place, make sure all of your dependencies are sorted upon installation, and make the installation of your software a single command (or click) away.&lt;/p&gt;&lt;p&gt;They also serve an important role as the user’s advocate. If an update ships which breaks a bunch of other packages, they’ll be in the trenches dealing with it so that the users don’t face the breakage themselves. They are also the first line of defense preventing the installation of malware on the user’s system. Many sideloaded packages for Linux include &lt;del&gt;telemetry&lt;/del&gt; spyware or adware from the upstream distributor, which is usually patched out by the distribution.&lt;/p&gt;&lt;p&gt;Distributions are also working on innovative projects at the scale of the entire software ecosystem, and are dealing with bigger picture things than you need to concern yourself with. Here are some things which they have already solved:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Automatic updates and dependency management&lt;/li&gt;&lt;li&gt;Universal cryptographic signatures for all packages&lt;/li&gt;&lt;li&gt;Worldwide distribution and bandwidth sharing via mirrors&lt;/li&gt;&lt;li&gt;System-wide audits of software installed on your machine&lt;/li&gt;&lt;li&gt;CVE management and patch distribution&lt;/li&gt;&lt;li&gt;Long-term support&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There are several areas of open research, too, such as reproducible builds or deterministic whole-system configuration like Nix and Guix are working on. You can take advantage of all of this innovation and research for the low price of zero dollars by standing back and letting distros handle the distribution of your software. It’s what they’re good at.&lt;/p&gt;&lt;p&gt;There are a few things you &lt;em&gt;can&lt;/em&gt; do to make this work better.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Ship your software as a simple tarball. Don’t ship pre-built binaries and definitely don’t ship a “curl | bash” command. Naive users will mess up their systems when they use them.&lt;/li&gt;&lt;li&gt;Use widely adopted, standard build systems and methodologies. Use the standard approach for your programming language. They have already been through the gamut of distros and their operating modes are well-understood by packagers.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2021/05/19/How-to-write-release-notes.html&quot; target=&quot;_blank&quot;&gt;Ship good release notes&lt;/a&gt;. Distro packagers read them! Give them a head’s up about any important changes which might affect their distro.&lt;/li&gt;&lt;li&gt;Be picky with your dependencies and try to avoid making huge dependency trees. Bonus: this leads to better security and maintainability!&lt;/li&gt;&lt;li&gt;Maintain a friendly dialogue with distro maintainers if and when they come asking questions. They’re the expert on their distro, but you’re the expert on your software, and sometimes you will meet to compare notes.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;em&gt;See also: &lt;a href=&quot;https://archive.fosdem.org/2018/schedule/event/how_to_make_package_managers_cry/&quot; target=&quot;_blank&quot;&gt;FOSDEM 2018 - How To Make Package Managers Cry&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;One thing you shouldn’t do is go around asking distros to add your program to their repos. Once you ship your tarballs, your job is done. It’s the &lt;em&gt;users&lt;/em&gt; who will go to their distro and ask for a new package. And users — do this! If you find yourself wanting to use some cool software which isn’t in your distro, go ask for it, or better yet, package it up yourself. For many packages, this is as simple as copying and pasting a similar package (let’s hope they followed my advice about using an industry-standard build system), making some tweaks, and building it.&lt;/p&gt;&lt;p&gt;Distros are quite accessible projects, packaging is usually not that difficult. Distributions always need more volunteers, and there are plenty of friendly experts at your local distro who would be pleased to help you figure out the finer details, assuming you’re prepared to stand up and do the work yourself. Once you get used to it, making and submitting a new package can take as little as 10 or 15 minutes for a simple one.&lt;/p&gt;&lt;p&gt;Oh, and if you are in the developer role — you are presumably also a user of both your own software and some kind of software distribution. This puts you in a really good position to champion it for inclusion in your own distro :)&lt;/p&gt;&lt;hr&gt;&lt;p&gt;P.S. Systems which invert this model, e.g. Flatpak, are completely missing the point.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Let-distros-do-their-job/</link>
        
        <pubDate>Mon, 27 Sep 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Let-distros-do-their-job/</guid>
      </item>
    
      <item>
        
        
          <title>Nitter and other Internet reclamation projects</title>
          <description>
            &lt;p&gt;The world wide web has become an annoying, ultra-commercialized space. Many websites today are prioritizing the interests of the company behind the domain, at the expense of the user’s experience and well-being. This has been a frustrating problem for several years, but lately there’s been a heartwarming trend of users fighting back against the corporate web and stepping up to help and serve each other’s needs in spite of them, through what I’ve come to think of as Internet reclamation projects.&lt;/p&gt;&lt;p&gt;I think the first of these which appeared on my radar was &lt;a href=&quot;https://github.com/iv-org/invidious&quot; target=&quot;_blank&quot;&gt;Invidious&lt;/a&gt;, which scrapes information off of a YouTube page and presents it in a more pleasant, user-first interface— something which &lt;a href=&quot;https://drewdevault.com/2019/04/02/NewPipe-represents-the-best-of-FOSS.html&quot; target=&quot;_blank&quot;&gt;NewPipe&lt;/a&gt; also does well for Android. These tools pry data out of YouTube’s hands and present it on a simple UI, designed for users first, with no ads or spyware, and with nice features YouTube would never add, like download links, audio mode, and offline viewing. It shows us what users want, but YouTube refuses to give.&lt;/p&gt;&lt;p&gt;Another project which has been particularly successful recently is &lt;a href=&quot;https://github.com/zedeus/nitter&quot; target=&quot;_blank&quot;&gt;Nitter&lt;/a&gt;, which does something similar for Twitter. Twitter’s increasingly draconian restrictions on who can access what data, and their attitude towards logged-out users in particular, has been a great annoyance to anyone who does not have, and does not want, a Twitter account, but who may still encounter Twitter links around the web. Nitter has been quite helpful in de-crapifying Twitter for these folks. I have set up an automatic redirect in my browser which takes me straight to Nitter, and I never have to see the shitty, user-hostile Twitter interface again.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://sr.ht/~cadence/bibliogram/&quot; target=&quot;_blank&quot;&gt;Bibliogram&lt;/a&gt; is another attempt which has done its best to fix Instagram, but they have &lt;a href=&quot;https://git.sr.ht/~cadence/bibliogram-docs/tree/master/docs/Instagram%20rate%20limits.md#tldr-what-does-it-mean-if-an-instance-is-blocked&quot; target=&quot;_blank&quot;&gt;encountered challenges&lt;/a&gt; with Instagram’s strict rate limits and anti-scraping measures. Another project, &lt;a href=&quot;https://codeberg.org/teddit/teddit&quot; target=&quot;_blank&quot;&gt;Teddit&lt;/a&gt;, is attempting to fix Reddit’s increasingly anti-user interface, and &lt;a href=&quot;https://github.com/spikecodes/libreddit&quot; target=&quot;_blank&quot;&gt;Libreddit&lt;/a&gt; has similar ambitions.&lt;/p&gt;&lt;p&gt;All of these services are more useful, more accessible, and more inclusive than their corporate counterparts. They work better on older browsers and low-end devices. They have better performance. They aren’t spying on you. In short, they are rejecting the &lt;a href=&quot;https://seirdy.one/2021/01/27/whatsapp-and-the-domestication-of-users.html&quot; target=&quot;_blank&quot;&gt;domestication of their users&lt;/a&gt; that the platforms they interact with have been trying to do. Their efforts are part of an inspiring trend of internet activism which rejects the corporate shells and walled gardens without giving up the useful data they have stolen away inside.&lt;/p&gt;&lt;p&gt;Here are some more services full of user-hostile behavior I’d like to see replaced with user-first, high performance, FOSS frontends:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Facebook&lt;/li&gt;&lt;li&gt;GitLab and GitHub&lt;/li&gt;&lt;li&gt;&lt;del&gt;Medium et al&lt;/del&gt; 2021-11-08: Check out &lt;a href=&quot;https://scribe.rip&quot; target=&quot;_blank&quot;&gt;scribe.rip&lt;/a&gt;!&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I would be happy to redirect myself away from any of these services for a faster, lighter weight, more inclusive, user-first experience. Any others you’d like to see?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Nitter-and-other-internet-reclamation-projects/</link>
        
        <pubDate>Thu, 23 Sep 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Nitter-and-other-internet-reclamation-projects/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, September 2021</title>
          <description>
            &lt;p&gt;It’s a quiet, foggy morning here in Amsterdam, and here with my fresh mug of coffee and a cuddly cat in my lap, I’d like to share the latest news on my FOSS efforts with you. Grab yourself a warm drink and a cat of your own and let’s get started.&lt;/p&gt;&lt;p&gt;First, a new project: &lt;a href=&quot;https://sr.ht/~sircmpwn/visurf&quot; target=&quot;_blank&quot;&gt;visurf&lt;/a&gt;. I &lt;a href=&quot;https://drewdevault.com/2021/09/11/visurf-announcement.html&quot; target=&quot;_blank&quot;&gt;announced this&lt;/a&gt; a few days ago, but the short of it is that I am building a minimal Wayland-only frontend for the &lt;a href=&quot;http://www.netsurf-browser.org&quot; target=&quot;_blank&quot;&gt;NetSurf&lt;/a&gt; web browser which uses vi-inspired keybindings. Since the announcement there has been some good progress: touch support, nsvirc, tabs, key repeat, and so on. Some notable medium-to-large efforts ahead of us include a context menu on right click, command completion and history, kinetic scrolling via touch, pinch-to-zoom, clipboard support, and a readability mode. Please help! It’s pretty easy to get involved: join the IRC channel at #netsurf on libera.chat and ask for something to do.&lt;/p&gt;&lt;p&gt;The programming language is also doing well. Following the codegen rewrite we have completed some long-pending refactoring to parts of the language design, which we intend to keep working on with further refinements in the coming weeks and months. We also developed a new frontend for reading the documentation in your terminal:&lt;/p&gt;&lt;script id=&quot;asciicast-q53ZaG138sp89gKYqo1fui9Qj&quot; src=&quot;https://asciinema.org/a/q53ZaG138sp89gKYqo1fui9Qj.js&quot; async&gt;&lt;/script&gt;
&lt;p&gt;Other improvements include the addition of parametric format modifiers (&lt;code&gt;fmt::printfln(&amp;quot;{%}&amp;quot;, 10, &amp;amp;fmt::modifiers { base = strconv::base::HEX, ...  })&lt;/code&gt;), fnmatch, and (WIP) design improvements to file I/O, the latter relying on new struct subtyping semantics. I’m hoping that we’ll have improvements to the grammar and semantics of match expressions and tagged unions in the near future, and we are also looking into some experiments with reflection.&lt;/p&gt;&lt;p&gt;Many improvements have landed for SourceHut. lists.sr.ht now has a writable GraphQL API, along with the first implementation of &lt;a href=&quot;https://sourcehut.org/blog/2021-08-25-graphql-native-webhooks/&quot; target=&quot;_blank&quot;&gt;GraphQL-native webhooks&lt;/a&gt;. Thanks to a few contributors, you can also now apply custom sorts to your search results on todo.sr.ht, and builds.sr.ht has grown Rocky Linux support. More details to follow in the “What’s cooking” post for the SourceHut blog.&lt;/p&gt;&lt;p&gt;That’s all for today! Thanks for tuning in for this update, and thanks for continuing to support our efforts. Have a great day!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-September-2021/</link>
        
        <pubDate>Wed, 15 Sep 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-September-2021/</guid>
      </item>
    
      <item>
        
        
          <title>visurf, a web browser based on NetSurf</title>
          <description>
            &lt;p&gt;I’ve started a new side project that I would like to share with you: &lt;a href=&quot;https://sr.ht/~sircmpwn/visurf&quot; target=&quot;_blank&quot;&gt;visurf&lt;/a&gt;. visurf, or nsvi, is a &lt;a href=&quot;https://www.netsurf-browser.org&quot; target=&quot;_blank&quot;&gt;NetSurf&lt;/a&gt; frontend which provides vi-inspired key bindings and a lightweight Wayland UI with few dependencies. It’s still a work-in-progress, and is not ready for general use yet. I’m letting you know about it today in case you find it interesting and want to help.&lt;/p&gt;&lt;p&gt;NetSurf is a project which has been on my radar for some time. It is a small web browser engine, developed in C independently of the lineage of WebKit and Gecko which defines the modern web today. It mostly supports HTML4 and CSS2, plus only a small amount of HTML5 and CSS3. Its JavaScript support, while present, is very limited. Given the &lt;a href=&quot;https://drewdevault.com/2020/03/18/Reckless-limitless-scope.html&quot; target=&quot;_blank&quot;&gt;epidemic of complexity in the modern web&lt;/a&gt;, I am pleased by the idea of a small browser, more limited in scope, which perhaps requires the cooperation of like-minded websites to support a pleasant experience.&lt;/p&gt;&lt;p&gt;I was a &lt;a href=&quot;https://qutebrowser.org&quot; target=&quot;_blank&quot;&gt;qutebrowser&lt;/a&gt; user for a long time, and I think it’s a great project given the constraints that it’s working in — namely, the modern web. But I reject the modern web, and qute is just as much a behemoth of complexity as the rest of its lot. Due to stability issues, I finally ended up abandoning it for Firefox several months ago.&lt;/p&gt;&lt;p&gt;The UI paradigm of qutebrowser’s modal interface, inspired by vi, is quite nice. I tried to use Tridactyl, but it’s a fundamentally crippled experience due to the limitations of Web Extensions on Firefox. Firefox has more problems besides — it may be somewhat more stable, but it’s ultimately still an obscenely complex, monsterous codebase, owned by an organization which cares less and less about my needs with each passing day. A new solution is called for.&lt;/p&gt;&lt;p&gt;Here’s where visurf comes in. Here’s a video of it in action:&lt;/p&gt;&lt;p&gt;&lt;video controls&gt;
&lt;source src=&quot;https://redacted.moe/f/5f0fb143.webm&quot;&gt;
&lt;/video&gt;&lt;/p&gt;&lt;p&gt;I hope that this project will achieve these goals:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a nice new web browser&lt;/li&gt;&lt;li&gt;Drive interest in the development of NetSurf&lt;/li&gt;&lt;li&gt;Encourage more websites to build with scope-constrained browsers in mind&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The first goal will involve fleshing out this web browser, and I could use your help. Please join #netsurf on irc.libera.chat, &lt;a href=&quot;https://todo.sr.ht/~sircmpwn/visurf&quot; target=&quot;_blank&quot;&gt;browse the issue tracker&lt;/a&gt;, and &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/visurf-devel&quot; target=&quot;_blank&quot;&gt;send patches&lt;/a&gt; if you are able. Some features I have in mind for the future are things like interactive link selection, a built-in readability mode to simplify the HTML of articles around the web, and automatic redirects to take advantage of tools like &lt;a href=&quot;https://github.com/zedeus/nitter&quot; target=&quot;_blank&quot;&gt;Nitter&lt;/a&gt;. However, there’s also more fundamental features to do, like clipboard support, command completion, even key repeat. There is much to do.&lt;/p&gt;&lt;p&gt;I also want to get people interested in improving NetSurf. I don’t want to see it become a “modern” web browser, and frankly I think that’s not even possible, but I would be pleased to see more people helping to improve its existing features, and expand them to include a reasonable subset of the modern web. I would also like to add Gemini support. I don’t know if visurf will ever be taken upstream, but I have been keeping in touch with the NetSurf team while working on it and if they’re interested it would be easy to see that through. Regardless, any improvements to visurf or to NetSurf will also improve the other.&lt;/p&gt;&lt;p&gt;To support the third goal, I plan on overhauling &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;sourcehut’s&lt;/a&gt; frontend&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, and in the course of that work we will be building a new HTML+CSS framework (think Bootstrap) which treats smaller browsers like NetSurf as a first-class target. The goal for this effort will be to provide a framework that allows for conservative use of newer browser features, with suitable fallbacks, with enough room for each website to express its own personality in a manner which is beautiful and useful on all manner of web browsers.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/visurf-announcement/</link>
        
        <pubDate>Sat, 11 Sep 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/visurf-announcement/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, August 2021</title>
          <description>
            &lt;p&gt;Greetings! It’s shaping up to be a beautiful day here in Amsterdam, and I have found the city much to my liking so far. If you’re in Amsterdam and want to grab a beer sometime, send me an email! I’ve been making a lot of new friends here. Meanwhile, I’ve also enjoyed a noticable increase in my productivity levels. Let’s go over the month’s accomplishments.&lt;/p&gt;&lt;p&gt;First, I have spent most of my time on the programming language project. I mentioned in the last update that we broke ground on a codegen rewrite, and yesterday all of our tests finally passed and I merged it. The new design is much better, and we should be able to simplify it even further still when we write the &lt;abbr title=&quot;The &apos;hosted&apos; compiler is one that &apos;hosts&apos; itself: it&apos;s written in our new programming language and compiles itself. This is contrasted with the &apos;bootstrap&apos; compiler, which is written in C and is used for bootstrapping the hosted toolchain from scratch.&quot;&gt;hosted compiler&lt;/abbr&gt; in the near future. This will also give us a better basis for a small number of experiments we’d like to do before finalizing the language design. Some other improvements include fleshing out our floating point math support library, a base64 module, a poll module, and parallel DNS resolution.&lt;/p&gt;&lt;p&gt;In SourceHut news, we shipped the &lt;a href=&quot;https://man.sr.ht/lists.sr.ht/graphql.md&quot; target=&quot;_blank&quot;&gt;lists.sr.ht GraphQL API&lt;/a&gt;. Future work will expand support for thread parsing and implement write operations. Presently, I am also working on a design for GraphQL-native webhooks, targetting meta.sr.ht for the initial release. sr.ht packages for Alpine 3.14 were now made available, and planned maintenance two weeks ago was the first of two fleet-wide rollouts of the upgrades to sr.ht hosted — the next is scheduled for tomorrow.&lt;/p&gt;&lt;p&gt;These two projects are my primary focus right now, and they’re both making good progress. In the coming month, I hope to address a few language design questions and build a more sophisticated I/O abstraction for the standard library. On sr.ht, I plan on expanding the GraphQL-native webhooks prototype and hopefully shipping it to one of the GQL APIs, along with starting on another major GQL support movement — either write support for lists.sr.ht, or the initial paste.sr.ht GQL API.&lt;/p&gt;&lt;p&gt;That’s all I have to share today! Thanks for tuning in.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-August-2021/</link>
        
        <pubDate>Sun, 15 Aug 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-August-2021/</guid>
      </item>
    
      <item>
        
        
          <title>Tips for debugging your new programming language</title>
          <description>
            &lt;p&gt;Say you’re building a new (compiled) programming language from scratch. You’ll inevitably have to debug programs written in it, and worse, many of these problems will lead you into deep magic, as you uncover problems with your compiler or runtime. And as you find yourself diving into the arcane arts, your tools may be painfully lacking: how do you debug code written in a language for which debuggers and other tooling simply has not been written yet?&lt;/p&gt;&lt;p&gt;In the implementation of my own programming language, I have faced this problem many times, and developed, by necessity, some skills around debugging with crippled tools that may lack an awareness of your language. Of course, the ultimate goal is to build out first-class debugging support, but we must have a language in the first place before we can write tools to debug it. If you find yourself in this situation, here are my recommendations.&lt;/p&gt;&lt;p&gt;First, I’ll echo the timeless words of Brian Kernighan:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;— Unix for Beginners (1979)&lt;/p&gt;&lt;p&gt;Classic debugging techniques are of heightened importance in this environment: first seek to isolate the problem code, then to understand the problem code, then form, and test, a hypothesis — usually with a thoughtful print statement. Often, this is enough.&lt;/p&gt;&lt;p&gt;Unfortunately, you may have to fire up gdb. gdb is often painful in the best of situations, but if you have to use it without debug symbols, you may find yourself shutting off the computer and seeking out rural real estate on which you can establish a new career in farming. If you can stomach it, I can offer some advice.&lt;/p&gt;&lt;p&gt;First, you’re going to be working in assembly, so make sure you’re familiar with how it works. I would recommend keeping the ISA manual and your ABI specification handy. If you’re smart and your language sets up stack frames properly (this is easy, do it early), you should at least have a backtrace, breakpoints at functions, and globals, though all of these will be untyped. You can write C casts to add some ad-hoc types to examine data in your process, like “print *(int *)$rdi”.&lt;/p&gt;&lt;p&gt;You’ll also get used to the ‘x’ command, which eXamines memory. The command format is “x/NT”, where N is the number of objects, and T is the object type: w for word (int), g for giantword (long), and h and b for halfword (short) and byte, respectively: “x/8g $rdi” will interpret rdi as an address where 8 longs are stored and print them out in hexadecimal. Of particular use is the “i” format, for “instruction”, which will disassemble from the given address:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;(gdb) x/8i $rip
=&amp;gt; 0x5555555565c8 &amp;lt;rt.memcpy+4&amp;gt;:	mov    $0x0,%eax
   0x5555555565cd &amp;lt;rt.memcpy+9&amp;gt;:	cmp    %rdx,%rax
   0x5555555565d0 &amp;lt;rt.memcpy+12&amp;gt;:	jae    0x5555555565df &amp;lt;rt.memcpy+27&amp;gt;
   0x5555555565d2 &amp;lt;rt.memcpy+14&amp;gt;:	movzbl (%rsi,%rax,1),%ecx
   0x5555555565d6 &amp;lt;rt.memcpy+18&amp;gt;:	mov    %cl,(%rdi,%rax,1)
   0x5555555565d9 &amp;lt;rt.memcpy+21&amp;gt;:	add    $0x1,%rax
   0x5555555565dd &amp;lt;rt.memcpy+25&amp;gt;:	jmp    0x5555555565cd &amp;lt;rt.memcpy+9&amp;gt;
   0x5555555565df &amp;lt;rt.memcpy+27&amp;gt;:	leave  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can set breakpoints on the addresses you find here (e.g. “b *0x5555555565d0”), and step through one instruction at a time with the “si” command.&lt;/p&gt;&lt;p&gt;I also tend to do some silly workarounds to avoid having to read too much assembly. If I want to set a breakpoint in some specific place, I might do the following:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;_break&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;comment spell&quot;&gt;// ...some code...&lt;/span&gt;

    &lt;span class=&quot;comment spell&quot;&gt;// Point of interest&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_break&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;somefunc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;comment spell&quot;&gt;// ...some code...&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I can instruct gdb to “b _break” to break when this function is called, use “finish” to step out of the call frame, and I’ve arrived at the point of interest without having to rely on line numbers being available in my binary.&lt;/p&gt;&lt;p&gt;Overall, this is a fairly miserable process which can take 5-10× longer than normal debugging, but with these tips you should at least find your problems solvable. Good motivation to develop better debugging tools for your new language, eh? A future blog post might go over some of this with DWARF and possibly how to teach gdb to understand a new language natively. In the meantime, good luck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Debugging-your-new-PL/</link>
        
        <pubDate>Wed, 11 Aug 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Debugging-your-new-PL/</guid>
      </item>
    
      <item>
        
        
          <title>Police to begin regular, warrant-free searches of homes for child abuse material</title>
          <description>
            &lt;p&gt;The Federal Bureau of Investigations announced a new initiative today to combat the proliferation of child sexual abuse materials (CSAM) in the United States. Starting next year, police will be conducting regular searches of US homes, as often as once or twice per week per home, to find child sexual abuse materials. This initiative will bring more child abusers to justice and help abuse victims to find solace in the knowledge that records of their abuse are not being shared in perpetuity.&lt;/p&gt;&lt;p&gt;To facilitate frequent and convenient searches, the FBI will be working with lock manufacturers to institute a new standard for home locks in the United States which permits their use via a “master key”, to be held securely by authorized government employees only. These new locks will become mandatory for all new homes next year, and a gradual process of retrofitting will take place in existing homes with the goal of having the program up to its full throughput no later than 2024.&lt;/p&gt;&lt;p&gt;In response to questions raised by &lt;del&gt;child abuse apologists&lt;/del&gt; concerned privacy advocates, the director of the FBI stated in a press conference:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Of course, for citizens who do not possess images of child sexual abuse, there is no cause for concern. Search operatives will undergo a mandatory 2 hour training course, and will be instructed to disregard anything they find or learn in the course of their searches which does not involve CSAM. Through our partnership with industry leaders in home security, we will make the process as safe and convenient as possible, so that authorized officers may enter your home at any time and quietly conduct their business without disturbing your day. We are excited about this unprecedented opportunity to curb the distribution of child abuse material in this country.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;In short, government officials are confident that the possibility of having your home searched at any time will ultimately pose little to no inconvenience to Americans, particularly with respect to the things they choose to do, people they choose to associate with, and things they say to their family and friends in &lt;del&gt;the privacy of&lt;/del&gt; their homes.&lt;/p&gt;&lt;p&gt;The director also noted the numerous jobs which will be created to fill the increased demand for officers, and petitioned congress for the appropriate increase to their budget.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;…wait. This &lt;em&gt;is&lt;/em&gt; happening, but I got some of the details wrong.&lt;/p&gt;&lt;p&gt;It’s not homes which are being searched, but the digital devices we use for all of our communication and information storage and retrieval needs in contemporary life.&lt;/p&gt;&lt;p&gt;And it’s not lock manufacturers that are making it possible, but Apple. And the government didn’t ask: they volunteered.&lt;/p&gt;&lt;p&gt;And it’s not police officers, but a proprietary machine learning algorithm that no one understands.&lt;/p&gt;&lt;p&gt;Oh, and it’s not happening one or two times a week, but on an ongoing basis, every time you use your device.&lt;/p&gt;&lt;p&gt;I did get a few things, right though. The only thing which limits the scope of searches will be whichever things Apple chooses to search or not to search. And whatever Congress demands they repurpose the system to use. Ah — and it is warrant-free.&lt;/p&gt;&lt;p&gt;Won’t you think of the children?&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.apple.com/child-safety/pdf/CSAM_Detection_Technical_Summary.pdf&quot; target=&quot;_blank&quot;&gt;source&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Apple-CSAM-scanning/</link>
        
        <pubDate>Tue, 10 Aug 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Apple-CSAM-scanning/</guid>
      </item>
    
      <item>
        
        
          <title>proxy.golang.org allows many Go packages to be silently broken</title>
          <description>
            &lt;p&gt;GOPROXY (or &lt;a href=&quot;https://proxy.golang.org&quot; target=&quot;_blank&quot;&gt;proxy.golang.org&lt;/a&gt;) is a service through which all “go get” commands (and other module downloads) are routed. It may speed up some operations by providing a cache, and it publishes checksums and an “index” of all Go packages; but this is done at the cost of sending details of all of your module downloads to Google and imposing extra steps when using Go packages from an intranet.&lt;/p&gt;&lt;p&gt;This cache never expires, which can cause some problems: you can keep fetching a module from proxy.golang.org long after the upstream version has disappeared. The upstream author probably had a good reason for removing a version! Because I set &lt;code&gt;GOPROXY=direct&lt;/code&gt; in my environment,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; which bypasses the proxy, I’ve been made aware of a great number of Go packages which have broken dependencies and are none the wiser. They generally can’t reproduce the problem without &lt;code&gt;GOPROXY=direct&lt;/code&gt;, which can make it a challenge to rouse up the enthusiasm for upstream to actually fix the issue. Caching modules forever can encourage bitrot.&lt;/p&gt;&lt;p&gt;Packages which have these issues cannot be built unless Google keeps the cache valid forever and can be trusted to treat the personal data associated with the request with respect. Furthermore, as soon as a debugging session finds its way to an absent module, you could be surprised to find that upstream is gone and that fetching or patching the code may be a challenge. This has created ticking time bombs throughout the Go ecosystem, which go undetected because GOPROXY hides the problem from developers.&lt;/p&gt;&lt;p&gt;If you want to check if your packages are affected by this, just set &lt;code&gt;GOPROXY=direct&lt;/code&gt; in your environment, blow away your local cache, and build your packages again. You might uncover an unpleasant surprise.&lt;/p&gt;&lt;p&gt;It may be worth noting that I already have a poor opinion of the Go module mirror — it’s been DDoS’ing my servers since February.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;a href=&quot;https://github.com/golang/go/issues/44577&quot; target=&quot;_blank&quot;&gt;Since I reported this&lt;/a&gt;, the Go team has been very opaque and non-communicative, and none of their mitigations have had a meaningful improvement. Most of the traffic is redundant — many modules are downloaded over and over again in short time intervals. I have the option of blocking their traffic, of course, but that would also block all Go programmers from fetching modules from my service. I hope they adopt my recommendation of allowing admins to configure the crawl parameters via robots.txt.&lt;/p&gt;&lt;p&gt;But, to be honest, the Go module mirror might not need to exist at all.&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;P.S. Do you have feedback on this post?&lt;/summary&gt;
  &lt;p&gt;
  I said, in
  &lt;a
    href=&quot;https://drewdevault.com/2021/04/26/Cryptocurrency-is-a-disaster.html&quot;
  &gt;Cryptocurrency is an abject disaster&lt;/a&gt;, that I wanted to make my blog
  more constructive. As it necessarily required a critical tone, this post might
  have broken this promise. Taking extra care to avoid this, I made an effort to
  use measured, reasonable language, to address specific problems rather than
  making generalizations, and to avoid flamebait, and I sought second opinions
  on the article before publishing.
  &lt;/p&gt;
  &lt;p&gt;
  I would welcome your feedback on the results. Was this post constructive?
  Should I instead refrain from this kind of criticism in general? Do you have
  any other thoughts to share? Please &lt;a
    href=&quot;mailto:drew@ddevault.org&quot;
  &gt;email me&lt;/a&gt; if so.
  &lt;/p&gt;
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/goproxy-breaks-go/</link>
        
        <pubDate>Fri, 06 Aug 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/goproxy-breaks-go/</guid>
      </item>
    
      <item>
        
        
          <title>In praise of PostgreSQL</title>
          <description>
            &lt;p&gt;After writing &lt;a href=&quot;gemini://drewdevault.com/2021/05/06/Praise-for-Alpine-Linux.gmi&quot; target=&quot;_blank&quot;&gt;Praise for Alpine Linux&lt;/a&gt;, I have decided to continue writing more articles in praise of good software. Today, I’d like to tell you a bit about &lt;a href=&quot;https://www.postgresql.org&quot; target=&quot;_blank&quot;&gt;PostgreSQL&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Many people don’t understand how old Postgres truly is: the first release&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; was in July of 1996. It used this logo:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/f36a9083.jpg&quot;&gt;
&lt;figcaption&gt;A “logo” which depicts the word “PostgreSQL” in a 3D chrome font bursting through a brick wall from space. No, seriously.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;After 25 years of persistence, and a better logo design, Postgres stands today as one of the most significant pillars of profound achievement in free software, alongside the likes of Linux and Firefox. PostgreSQL has taken a complex problem and &lt;em&gt;solved&lt;/em&gt; it to such an effective degree that all of its competitors are essentially obsolete, perhaps with the exception of SQLite.&lt;/p&gt;&lt;p&gt;For a start, Postgres is simply an incredibly powerful, robust, and reliable piece of software, providing the best implementation of SQL.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; It provides a great deal of insight into its own behavior, and allows the experienced operator to fine-tune it to achieve optimal performance. It supports a broad set of SQL features and data types, with which I have always been able to efficiently store and retrieve my data. SQL is usually the #1 bottleneck in web applications, and Postgres does an excellent job of providing you with the tools necessary to manage that bottleneck.&lt;/p&gt;&lt;p&gt;Those tools are also exceptionally well-documented. The &lt;a href=&quot;https://www.postgresql.org/docs/current/index.html&quot; target=&quot;_blank&quot;&gt;PostgreSQL documentation&lt;/a&gt; is &lt;em&gt;incredibly&lt;/em&gt; in-depth. It puts the rest of us to shame, really. Not only do they have comprehensive reference documentation which exhaustively describes every feature, but also vast amounts of prose which explains the internal design, architecture, and operation of Postgres, plus detailed plain-English explanations of how various high-level tasks can be accomplished, complete with the necessary background to &lt;em&gt;understand&lt;/em&gt; those tasks. There’s essentially no reason to ever read a blog post or Stack Overflow answer about how to do something with Postgres — the official docs cover every aspect of the system in great depth.&lt;/p&gt;&lt;p&gt;The project is maintained by a highly disciplined team of engineers. I have complete confidence in their abilities to handle matters of performance, regression testing, and security. They publish meticulously detailed weekly development updates, as well as thorough release notes that equips you with sufficient knowledge to confidently run updates on your deployment. Their git discipline is also legendary — here’s the &lt;a href=&quot;https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=aa769f80ed80b7adfbdea9a6bc267ba4aeb80fd7&quot; target=&quot;_blank&quot;&gt;latest commit&lt;/a&gt; at the time of writing:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;postgres_fdw: Fix issues with generated columns in foreign tables.

postgres_fdw imported generated columns from the remote tables as plain
columns, and caused failures like &amp;quot;ERROR: cannot insert a non-DEFAULT
value into column &amp;quot;foo&amp;quot;&amp;quot; when inserting into the foreign tables, as it
tried to insert values into the generated columns.  To fix, we do the
following under the assumption that generated columns in a postgres_fdw
foreign table are defined so that they represent generated columns in
the underlying remote table:

* Send DEFAULT for the generated columns to the foreign server on insert
  or update, not generated column values computed on the local server.
* Add to postgresImportForeignSchema() an option &amp;quot;import_generated&amp;quot; to
  include column generated expressions in the definitions of foreign
  tables imported from a foreign server.  The option is true by default.

The assumption seems reasonable, because that would make a query of the
postgres_fdw foreign table return values for the generated columns that
are consistent with the generated expression.

While here, fix another issue in postgresImportForeignSchema(): it tried
to include column generated expressions as column default expressions in
the foreign table definitions when the import_default option was enabled.

Per bug #16631 from Daniel Cherniy.  Back-patch to v12 where generated
columns were added.

Discussion: https://postgr.es/m/16631-e929fe9db0ffc7cf%40postgresql.org
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://git.postgresql.org/gitweb/?p=postgresql.git&quot; target=&quot;_blank&quot;&gt;They’re all like this&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Ultimately, PostgreSQL is a technically complex program which requires an experienced and skilled operator to be effective. Learning to use it is a costly investment, even if it pays handsomely. Though Postgres has occasionally frustrated or confused me, on the whole my feelings for it are overwhelmingly positive. It’s an incredibly well-made product and its enormous and still-growing successes are very well-earned. When I think of projects which have made the most significant impacts on the free software ecosystem, and on the world at large, PostgreSQL has a place on that list.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/In-praise-of-Postgres/</link>
        
        <pubDate>Thu, 05 Aug 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/In-praise-of-Postgres/</guid>
      </item>
    
      <item>
        
        
          <title>My wish-list for the next YAML</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;http://yaml.org&quot; target=&quot;_blank&quot;&gt;YAML&lt;/a&gt; is both universally used, and universally reviled. It has a lot of problems, but it also is so useful in solving specific tasks that it’s hard to replace. Some new kids on the block (such as TOML) have successfully taken over a &lt;em&gt;portion&lt;/em&gt; of its market share, but it remains in force in places where those alternatives show their weaknesses.&lt;/p&gt;&lt;p&gt;I think it’s clear to most that YAML is in dire need of replacement, which is why many have tried. But many have also failed. So what are the key features of YAML which demonstrate its strengths, and key weaknesses that could be improved upon?&lt;/p&gt;&lt;p&gt;Let’s start with some things that YAML does well, which will have to be preserved.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hierarchical relationships emphasized with whitespace&lt;/strong&gt;. There is no better way of representing a hierarchical data structure than by actually organizing your information visually. Note that semantically meaningful whitespace is not actually required — the use of tokens like { is acceptable — so long as, by convention, hierarchies are visually apparent.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Defined independently of its implementation&lt;/strong&gt;. There should not be a canonical implementation of the format (though a reference implementation is, perhaps, acceptable). It should not be defined as “a config library for $language”. Interoperability is key. It must have a specification.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easily embeds documents written in other formats&lt;/strong&gt;. This is the chief reason that YAML still dominates in CI configuration: the ability to trivially write scripts directly into config file, without escaping anything or otherwise molesting the script.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;string property&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;punctuation_delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;string property&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string punctuation_delimiter&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;
    jit_flags=&amp;quot;&amp;quot;
    if [ &amp;quot;$(uname -m)&amp;quot; != &amp;quot;x86_64&amp;quot; ]
    then
        jit_flags=--without-jit
    fi
    ./configure \
        --prefix=/usr \
        $jit_flags&lt;/span&gt;
&lt;span class=&quot;punctuation_delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;string property&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string punctuation_delimiter&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;
    make&lt;/span&gt;
&lt;span class=&quot;punctuation_delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;string property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string punctuation_delimiter&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;
    make check
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Both machine- and human-editable&lt;/strong&gt;. It’s very useful for both humans and machines to collaborate on a YAML file. For instance, humans write build manifests for their git.sr.ht repos, and then the project hub adds steps to download and apply patches from mailing lists before submitting them to the build driver. For the human’s part, the ability to easily embed scripts (see above) and write other config parameters conveniently is very helpful — everyone hates config.json.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Not a programming language&lt;/strong&gt;. YAML entities are a problem, but we’ll talk about that separately. In general, YAML files are not programs. They’re just data. This is a good thing. If you want, you can use a &lt;em&gt;separate&lt;/em&gt; pre-processor, like jsonnet.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What needs to be improved upon?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;A much simpler grammar&lt;/strong&gt;. No more billion laughs, please. Besides this, 90% of YAML’s features go un-used, which increases the complexity of implementations, not to mention their attack surface, for little reason.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;A means of defining a schema&lt;/strong&gt;, which can influence the interpretation of the input. YAML does this poorly. Consider the following YAML list:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;string property&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;punctuation_delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;punctuation_delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;24&lt;/span&gt;
&lt;span class=&quot;punctuation_delimiter&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;world&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two of these are strings, and one is a number. Representing numbers and strings plainly like this makes it easier for humans to write, though requiring humans to write their values in a format which provides an unambiguous type is not so inconvenient as to save this trait from the cutting room floor. Leaving the ambiguity in place, without any redress, provides a major source of bugs in programs that consume YAML.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;I don’t care about JSON interoperability&lt;/strong&gt;. Being a superset of JSON is mildly useful, but not so much so as to compromise any other features or design. I’m prepared to yeet it at the first sign of code smells.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Someday I may design something like this myself, but I’m really hoping that someone else does it instead. Good luck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-next-YAML/</link>
        
        <pubDate>Wed, 28 Jul 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-next-YAML/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, July 2021</title>
          <description>
            &lt;p&gt;Hallo uit Nederland! I’m writing to you from a temporary workstation in Amsterdam, pending the installation of a better one that I’ll put together after I visit a furniture store today. I’ve had to slow a few things down somewhat while I prepare for this move, and I’ll continue to be slower for some time following it, but things are moving along regardless.&lt;/p&gt;&lt;p&gt;One point of note is that the maintainer for &lt;a href=&quot;https://aerc-mail.org&quot; target=&quot;_blank&quot;&gt;aerc&lt;/a&gt;, Reto Brunner, has stepped down from his role. I’m looking for someone new to fill his shoes; please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;let me know&lt;/a&gt; if you are interested.&lt;/p&gt;&lt;p&gt;As far as the language project is concerned, there has been some significant progress. We’ve broken ground on the codegen rewrite, and it’s looking much better than its predecessor. I expect progress on this front to be fairly quick. In the meanwhile, a new contributor has come onboard to help with floating-point math operations, and I merged their first patch this morning — adding math::abs, math::copysign, etc. Another contributor has been working in a similar space, and sent in an f32-to-string function last week. I implemented DNS resolution and a “dial” function as well, which you can read about in my &lt;a href=&quot;https://drewdevault.com/blog/finger-client/&quot;&gt;previous post about a finger client&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I also started writing some POSIX utilities in the new language for fun:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;getopt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;utilmain&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;getopt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;getopt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1z&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Error opening &amp;apos;{}&amp;apos;: {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re still looking for someone to contribute in cryptography, and in date/time support — please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;let me know&lt;/a&gt; if you want to help.&lt;/p&gt;&lt;p&gt;In SourceHut news, I have mostly been focused on writing the GraphQL API for lists.sr.ht. I have made substantial progress, and I had hoped to ship the first version before publishing today’s status updates, but I was delayed due to concerns with the move abroad. I hope to also have sr.ht available for Alpine 3.14 in the near future.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-July-2021/</link>
        
        <pubDate>Thu, 15 Jul 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-July-2021/</guid>
      </item>
    
      <item>
        
        
          <title>Is GitHub a derivative work of GPL&apos;d software?</title>
          <description>
            &lt;p&gt;GitHub recently announced a tool called &lt;a href=&quot;https://copilot.github.com&quot; target=&quot;_blank&quot;&gt;Copilot&lt;/a&gt;, a tool which uses machine learning to provide code suggestions, inciting no small degree of controversy. One particular facet of the ensuing discussion piques my curiosity: what happens if the model was trained using software licensed with the GNU General Public License?&lt;/p&gt;&lt;p&gt;&lt;em&gt;Disclaimer: I am the founder of a company which competes with GitHub.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The GPL is among a family of licenses considered “copyleft”, which are characterized by their “viral” nature. In particular, the trait common to copyleft works is the requirement that “derivative works” are required to publish their new work under the same terms as the original copyleft license. Some weak copyleft licenses, like the Mozilla Public License, only apply to any changes to specific files from the original code. Stronger licenses like the GPL family affect the broader work that any GPL’d code has been incorporated into.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/mitsuhiko/status/1410886329924194309&quot; target=&quot;_blank&quot;&gt;A recent tweet by @mitsuhiko&lt;/a&gt; notes that Copilot can be caused to produce, verbatim, the famous fast inverse square root function from Quake III Arena: a codebase distributed under the GNU GPL 2.0 license. This raises an interesting legal question: is the work produced by a machine learning system, or even the machine learning system itself, a derivative work of the inputs to the model?  &lt;a href=&quot;https://twitter.com/eevee/status/1410037309848752128&quot; target=&quot;_blank&quot;&gt;Another tweet&lt;/a&gt; suggests that, if the answer is “no”, GitHub Copilot can be used as a means of washing the GPL off of code you want to use without obeying its license. But, what if the answer is “yes”?&lt;/p&gt;&lt;p&gt;I won’t take a position on this question&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, but I will point out something interesting: if the answer is “&lt;em&gt;yes&lt;/em&gt;, machine learning models create derivative works of their inputs”, then GitHub may itself now be considered a derivative work of copyleft software. Consider this statement from GitHub’s blog post on the subject:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;During GitHub Copilot’s early development, nearly 300 employees used it in their daily work as part of an internal trial.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;— &lt;a href=&quot;https://docs.github.com/en/github/copilot/research-recitation&quot; target=&quot;_blank&quot;&gt;Albert Ziegler: A first look at rote learning in GitHub Copilot suggestions&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If 300 GitHub employees used Copilot as part of their daily workflow, they are likely to have incorporated the output of Copilot into nearly every software property of GitHub, which provides network services to users. If the model was trained on software using the GNU Affero General Public License (AGPL), and the use of this model created a derivative work, this may entitle all GitHub users to receive a copy of GitHub’s source code under the terms of the AGPL, effectively forcing GitHub to become an open source project. I’m normally against GPL enforcement by means of pulling the rug out from underneath someone who made an honest mistake&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, but in this case it would certainly be a fascinating case of comeuppance.&lt;/p&gt;&lt;p&gt;Following the Copilot announcement, many of the ensuing discussions hinted to me at a broader divide in the technology community with respect to machine learning. I’ve seen many discussions having to wrestle with philosophical differences between participants, who give different answers to more fundamental questions regarding the ethics of machine learning: what rights should be, and are, afforded to the owners of the content which is incorporated into training data for machine learning? If I want to publish a work which I &lt;em&gt;don’t&lt;/em&gt; want to be incorporated into a model, or which, if used for a model, would entitle the public to access to that model, could I? Ought I be allowed to? What if the work being used is my personal information, collected without my knowledge or consent? What if the information is used against me, for example in making lending decisions? What if it’s used against society’s interests at large?&lt;/p&gt;&lt;p&gt;The differences of opinion I’ve seen in the discussions born from this announcement seem to suggest a substantial divide over machine learning, which the tech community may have yet to address, or even understand the depth of. I predict that GitHub Copilot will mark one of several inciting events which start to rub some of the glamour off of machine learning technology and gets us thinking about the ethical questions it presents.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Is-GitHub-a-derivative-work/</link>
        
        <pubDate>Sun, 04 Jul 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Is-GitHub-a-derivative-work/</guid>
      </item>
    
      <item>
        
        
          <title>How does IRC&apos;s federation model compare to ActivityPub?</title>
          <description>
            &lt;p&gt;Today’s federated revolution is led by ActivityPub, leading to the rise of services like Mastodon, PeerTube, PixelFed, and more. These new technologies have a particular approach to federation, which is coloring perceptions on what it actually means for a system to be federated at all. Today’s post will explain how &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Relay_Chat&quot; target=&quot;_blank&quot;&gt;Internet Relay Chat&lt;/a&gt; (IRC), a technology first introduced in the late 1980’s, does federation differently, and why.&lt;/p&gt;&lt;p&gt;As IRC has aged, many users today have only ever used a few networks, such as Liberachat (or Freenode, up until several weeks ago), which use a particular IRC model which does not, at first glance, appear to utilize federation. After all, everyone types “irc.libera.chat” into their client and they all end up on the same network and in the same namespace. However, this domain name is backed by a round-robin resolver which will connect you to any of &lt;a href=&quot;https://netsplit.de/servers/?net=Libera.Chat&quot; target=&quot;_blank&quot;&gt;several dozen servers&lt;/a&gt;, which are connected to each other&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and exchange messages on behalf of the users who reside on each. This is why we call them IRC &lt;em&gt;networks&lt;/em&gt; — each is composed of a network of servers that work together.&lt;/p&gt;&lt;p&gt;But why can’t I send messages to users on &lt;a href=&quot;https://www.oftc.net&quot; target=&quot;_blank&quot;&gt;OFTC&lt;/a&gt; from my Libera Chat session? Well, IRC networks are federated, but they are typically a &lt;em&gt;closed&lt;/em&gt; federation, such that each network forms a discrete graph of servers, not interconnected with any of the others. In ActivityPub terms, imagine a version of Mastodon where, instead of automatically federating with new instances, server operators whitelisted each one, forming a closed graph of connected instances. Organize these servers under a single named entity (“Mastonet” or something), and the result is an “ActivityPub network” which operates in the same sense as a typical “IRC network”.&lt;/p&gt;&lt;p&gt;In contrast to Mastodon’s open federation, allowing any server to peer with any others without prior agreement between their operators, most IRC networks are closed. The network’s servers may have independent operators, but they operate together under a common agreement, rather than the laissez-faire approach typical of&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; ActivityPub servers. The exact organizational and governance models vary, but many of these networks have discrete teams of staff which serve as moderators&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, often unrelated to the people responsible for the servers. The social system can be designed independently of the technology.&lt;/p&gt;&lt;p&gt;Among IRC networks, there are degrees of openness. Libera Chat, the largest network, is run by a single governing organization, using servers donated by (and in the possession of) independent sponsors. Many smaller networks are run on as few as one server, and some larger networks (particularly older ones) are run by many independent operators acting like more of a cooperative. &lt;a href=&quot;http://efnet.org&quot; target=&quot;_blank&quot;&gt;EFnet&lt;/a&gt;, the oldest network, is run in this manner — you can even &lt;a href=&quot;http://www.efnet.org/?module=docs&amp;doc=16&quot; target=&quot;_blank&quot;&gt;apply to become an operator&lt;/a&gt; yourself.&lt;/p&gt;&lt;p&gt;We can see from this that the idea of federation is flexible, allowing us to build a variety of social and operational structures. There’s no single right answer — approaches like IRC are able to balance many different benefits and drawbacks of their approach, such as balancing a reduced level of user mobility with a stronger approach to moderation and abuse reduction, while simultaneously enjoying the cost and scalability benefits of a federated design. Other federations, like Matrix, email, and Usenet, have their own set of tradeoffs. What unifies them is the ability to scale to a large size without expensive infrastructure, under the social models which best suit their users’ needs, without a centralizing capital motive.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-does-IRC-federate/</link>
        
        <pubDate>Sat, 03 Jul 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-does-IRC-federate/</guid>
      </item>
    
      <item>
        
        
          <title>You can&apos;t capture the nuance of my form fields</title>
          <description>
            &lt;p&gt;Check out this text box:&lt;/p&gt;&lt;style&gt;
textarea {
  width: 100%;
}
&lt;/style&gt;

&lt;textarea rows=&quot;10&quot; autocomplete=&quot;off&quot; autocorrect=&quot;off&quot; autocapitalize=&quot;off&quot; spellcheck=&quot;false&quot;&gt;Consectetur qui consequatur voluptatibus voluptatem sit sint perspiciatis. Eos aspernatur ad laboriosam quam numquam quo. Quia reiciendis illo quo praesentium. Dolor porro et et sit dolorem quisquam totam quae.
Ea molestias a aspernatur dignissimos suscipit incidunt. Voluptates in vel qui quaerat. Asperiores vel sit rerum est ipsam. Odio aut aut voluptate qui voluptatem.
Quia consequatur provident fugiat voluptatibus consequatur. Est sunt aspernatur velit. Officiis a dolorum accusantium. Sint est ut inventore.&lt;/textarea&gt;
&lt;p&gt;Here are some of the nuances of using this text box on my operating system (Linux) and web browser (Firefox):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Double clicking selects a word, and triple-clicking selects the whole line. If I double- or triple-click-and-hold, I can drag the mouse to expand the selection word-wise or line-wise, not just character-wise. This works with the paragraphs of text in the body of this blog post, too.&lt;/li&gt;&lt;li&gt;Holding control and pressing right will move move word-wise through the file. It always moves to the start or end of the next or prior word, so pressing “control+left, control+left, control+right” will end up in a different position than “control+left” alone. Adding “shift” to any of these will mutate the text selection.&lt;/li&gt;&lt;li&gt;Clicking any of the whitespace after the end of the text will put the cursor after the last character, even if you click to the left of the last character. This makes it easy to start appending text to the end.&lt;/li&gt;&lt;li&gt;Clicking and dragging from any point, I can drag the mouse straight upward, exceeding the bounds of the text box or even the entire web browser, to select all text from that point to the start of the text box. (Thanks minus for mentioning this one)&lt;/li&gt;&lt;li&gt;Selecting text and middle clicking anywhere will paste the text at the clicked location. This uses a separate, distinct clipboard from the one accessed with ctrl+c/ctrl+v. I can also use shift+insert to paste text from this secondary clipboard (this is called the “primary selection”).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I rely on all of these nuances when I use form controls in my everyday life. This is just for English, by the way. I often type in Japanese, which has an entirely alien set of nuances. Here’s what that looks like on Android (mobile is another beast entirely, too!):&lt;/p&gt;&lt;video src=&quot;https://redacted.moe/f/2f2f6815.webm&quot; muted autoplay loop controls&gt;
  If you&apos;re seeing this, your browser doesn&apos;t support HTML5 video, or webm, idk.
&lt;/video&gt;
&lt;p&gt;Here’s another control:&lt;/p&gt;&lt;select&gt;
&lt;option&gt;Alabama&lt;/option&gt;
&lt;option&gt;Alaska&lt;/option&gt;
&lt;option&gt;Arizona&lt;/option&gt;
&lt;option&gt;Arkansas&lt;/option&gt;
&lt;option&gt;California&lt;/option&gt;
&lt;option&gt;Colorado&lt;/option&gt;
&lt;option&gt;Connecticut&lt;/option&gt;
&lt;option&gt;Delaware&lt;/option&gt;
&lt;option&gt;Florida&lt;/option&gt;
&lt;option&gt;Georgia&lt;/option&gt;
&lt;option&gt;Hawaii&lt;/option&gt;
&lt;option&gt;Idaho&lt;/option&gt;
&lt;option&gt;Illinois&lt;/option&gt;
&lt;option&gt;Indiana&lt;/option&gt;
&lt;option&gt;Iowa&lt;/option&gt;
&lt;option&gt;Kansas&lt;/option&gt;
&lt;option&gt;Kentucky&lt;/option&gt;
&lt;option&gt;Louisiana&lt;/option&gt;
&lt;option&gt;Maine&lt;/option&gt;
&lt;option&gt;Maryland&lt;/option&gt;
&lt;option&gt;Massachusetts&lt;/option&gt;
&lt;option&gt;Michigan&lt;/option&gt;
&lt;option&gt;Minnesota&lt;/option&gt;
&lt;option&gt;Mississippi&lt;/option&gt;
&lt;option&gt;Missouri&lt;/option&gt;
&lt;option&gt;Montana&lt;/option&gt;
&lt;option&gt;Nebraska&lt;/option&gt;
&lt;option&gt;Nevada&lt;/option&gt;
&lt;option&gt;New Hampshire&lt;/option&gt;
&lt;option&gt;New Jersey&lt;/option&gt;
&lt;option&gt;New Mexico&lt;/option&gt;
&lt;option&gt;New York&lt;/option&gt;
&lt;option&gt;North Carolina&lt;/option&gt;
&lt;option&gt;North Dakota&lt;/option&gt;
&lt;option&gt;Ohio&lt;/option&gt;
&lt;option&gt;Oklahoma&lt;/option&gt;
&lt;option&gt;Oregon&lt;/option&gt;
&lt;option&gt;Pennsylvania&lt;/option&gt;
&lt;option&gt;Rhode Island&lt;/option&gt;
&lt;option&gt;South Carolina&lt;/option&gt;
&lt;option&gt;South Dakota&lt;/option&gt;
&lt;option&gt;Tennessee&lt;/option&gt;
&lt;option&gt;Texas&lt;/option&gt;
&lt;option&gt;Utah&lt;/option&gt;
&lt;option&gt;Vermont&lt;/option&gt;
&lt;option&gt;Virginia&lt;/option&gt;
&lt;option&gt;Washington&lt;/option&gt;
&lt;option&gt;West Virginia&lt;/option&gt;
&lt;option&gt;Wisconsin&lt;/option&gt;
&lt;option&gt;Wyoming&lt;/option&gt;
&lt;/select&gt;
&lt;p&gt;There’s an invisible edit buffer, so I can type “Pennsylvania” (or just P) to select what I want. I can type “New” and then press down to select “New Jersey”. If I make a mistake and I’ve kept track of what I’ve typed in my head, I can use backspace to make a correction, and it just works. I have lived in both of these places, and worked both of these keystrokes into my muscle memory. Filling out a form with my address on it and using an input box like this to select my state of residence takes me less than a second.&lt;/p&gt;&lt;p&gt;You cannot capture all of this nuance in a home-grown form control, or even anything close to it, but many JavaScript programmers do it anyway. Whenever I encounter a custom form control, the time required to complete the form increases from under a second to as much as a minute.&lt;/p&gt;&lt;p&gt;For myself, this is just very annoying. Imagine the same situation if you were blind. The standard form inputs work everywhere, and are designed with accessibility in mind, so you’re used to them and can easily fill in forms which use the standard browser controls. But, when you hit a JavaScript-powered organic cage-free non-GMO text box, you’re screwed.&lt;/p&gt;&lt;p&gt;There are hundreds of little nuances that users learn to use their computers efficiently. The exact features a user relies on will vary between operating systems, browsers, hardware, natural languages, physical ability, and personal preferences and experience. There are dozens of tiny workflows that people depend on every day that have never even occurred to you.&lt;/p&gt;&lt;p&gt;Making a custom form control with JavaScript is going to make life worse for a lot of people. Just don’t do it. The browser’s built-in controls are quite sufficient.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/You-cant-capture-the-nuance/</link>
        
        <pubDate>Sun, 27 Jun 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/You-cant-capture-the-nuance/</guid>
      </item>
    
      <item>
        
        
          <title>A finger client</title>
          <description>
            &lt;p&gt;This is a short follow-up to the &lt;a href=&quot;https://drewdevault.com/2021/05/24/io_uring-finger-server.html&quot; target=&quot;_blank&quot;&gt;io_uring finger server&lt;/a&gt; article posted about a month ago. In the time since, we have expanded our language with a more complete networking stack, most importantly by adding a DNS resolver. I have used these improvements to write a small client implementation of the &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1288&quot; target=&quot;_blank&quot;&gt;finger protocol&lt;/a&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;attribute&quot;&gt;@init&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;registersvc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;registersvc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;tcp&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;finger&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;79&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;attribute&quot;&gt;@noreturn&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Usage: {} &amp;lt;user&amp;gt;[@&amp;lt;host&amp;gt;]&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;tcp&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;finger&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;{}&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Technically, we could do more, but I chose to just address the most common use-case for finger servers in active use today: querying a specific user. Expanding this with full support for all finger requests would probably only grow this code by 2 or 3 times.&lt;/p&gt;&lt;p&gt;Our language now provides a net::dial module, inspired by &lt;a href=&quot;https://golang.org/pkg/net/#Dial&quot; target=&quot;_blank&quot;&gt;Go’s net.Dial&lt;/a&gt; and the &lt;a href=&quot;http://man.9front.org/2/dial&quot; target=&quot;_blank&quot;&gt;Plan 9 dial function&lt;/a&gt; Go is derived from. Our dial actually comes a bit closer to Plan 9 by re-introducing the service parameter — Plan 9’s “tcp!example.org!http” becomes net::dial(“tcp”, “example.org”, “http”) in our language — which we use to find the port (unless you specify it in the address). The service parameter is tested against a small internal list of known services, and against /etc/services. We also automatically perform an SRV lookup for “_finger._tcp.example.org”, so most programs written in our language will support SRV records with no additional effort.&lt;/p&gt;&lt;p&gt;In our client code, we can see that the @init function adds “finger” to the list of known internal services. @init functions run on start-up, and this one just lets dial know about our protocol. Our network stack is open to extension in other respects, too — unlike Go, third-party libraries can define new protocol handlers for dial as well, perhaps opening it up in the future to networks like AF_BLUETOOTH, AF_AX25, and so on, complete with support for network-appropriate addresses and resolver functionality.&lt;/p&gt;&lt;p&gt;The rest is pretty straightforward! We just parse the command line, dial the server, write the username to it, and splice the connection into stdout. Much simpler than the server. Future improvements might rewrite the CRLF to LF, but that’s not particularly important.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/finger-client/</link>
        
        <pubDate>Thu, 24 Jun 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/finger-client/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, June 2021</title>
          <description>
            &lt;p&gt;Hiya! Got another status update for you. First, let me share this picture that my dad and I took on our recent astronomy trip (click for full res):&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/o750.jpg&quot;&gt;
&lt;figcaption&gt;A long-exposure picture of the night sky. Thousands of stars are visible, as well as the band of the milky way.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Bonus Venus:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/6574aa37.png&quot;&gt;
&lt;figcaption&gt;A bright white circle against a dark background&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;So, what’s new? With SourceHut, there are a few neat goings-on. For one, thanks to Michael Forney putting the finishing touches on the patchset, the long-awaited NetBSD image is now available for builds.sr.ht. Also, the initial lists.sr.ht GraphQL API design is in place, and Simon Ser is working on a new and improved implementation of email discussion parsing for us to use. I’ve also redesigned the registration &amp; onboarding flow based on a maintainer/contributor distinction, which should help people understand how sourcehut works a bit better. Also, as promised, the writable GraphQL API for builds.sr.ht is now available.&lt;/p&gt;&lt;p&gt;I had been working on a new feature for the secret programming language, but in the course of implementing it, it became clear to me that we need to take a step back and do some deep refactoring in the compiler. This will probably occupy us for a couple of months. Even so, some improvements in the standard library have been made and shall continue to be made. You may have seen a few weeks ago that I &lt;a href=&quot;https://drewdevault.com/blog/io_uring-finger-server/&quot;&gt;wrote a finger server&lt;/a&gt; in the new language, and there’s a bunch of code for you to read there if you’re interested in learning more.&lt;/p&gt;&lt;p&gt;I also spent some time this month on Simon’s &lt;a href=&quot;https://git.sr.ht/~emersion/gamja&quot; target=&quot;_blank&quot;&gt;gamja&lt;/a&gt; and &lt;a href=&quot;https://git.sr.ht/~emersion/soju&quot; target=&quot;_blank&quot;&gt;soju&lt;/a&gt; projects. Libera.chat is running an experimental instance of gamja &lt;a href=&quot;https://web.libera.chat/gamja&quot; target=&quot;_blank&quot;&gt;for their webchat&lt;/a&gt;, and I’ve helped Simon incorporate some of their feedback and apply a layer of polish to the client. I’m also working on generalizing soju a bit so that we can eventually utilize it to offer a hosted IRC bouncer for sr.ht users.&lt;/p&gt;&lt;p&gt;That’s all I have to share for now. My foci have been on sourcehut and the secret language, and will continue to be those. I plan on advancing the work on the GraphQL APIs for sr.ht and ideally shipping an initial version of the lists.sr.ht API in a few weeks. I’ll share more news about the new language when it’s ready. Until next time!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-June-2021/</link>
        
        <pubDate>Tue, 15 Jun 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-June-2021/</guid>
      </item>
    
      <item>
        
        
          <title>Provided &quot;as is&quot;, without warranty of any kind</title>
          <description>
            &lt;p&gt;The MIT license contains the following text, in all uppercase no less:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The BSD licenses, GPL family of licenses, Apache 2.0, Mozilla Public License, and likely any other license you’d care to name, have similar clauses of their own. It’s worth taking a moment to consider the implications of this statement and what it says about the social aspects of free and open source software.&lt;/p&gt;&lt;p&gt;Many people who rely on free and open source software feel entitled to some degree of workitude or support from the developers, or think that the developers have a responsibility to provide good maintenance, or any maintenance at all, for their work. This is simply not true. All free and open source software disclaims all responsibility for your use of them for any purpose, often in all capital letters.&lt;/p&gt;&lt;p&gt;Some maintainers will allow you to negotiate additional terms with them, for example through the sale of a support contract, for which you may receive such a guarantee. If you have not made such an agreement with your maintainers, they have no responsibility to provide you with any support or assurance of quality. That means that they do not have to solve your bug reports or answer your questions. They do not have to review and apply your patch. They do not have to write documentation. They do not have to port it to your favorite platform. You are not entitled to the blood, sweat, and tears of the maintainers of the free &amp; open source software you use.&lt;/p&gt;&lt;p&gt;It is &lt;em&gt;nice&lt;/em&gt; when a maintainer offers you their time, but by no means are they required to. FOSS is what &lt;strong&gt;you&lt;/strong&gt; make of it. You have the right to make the changes you need from the software yourself, and you are the only person that you can reliably expect to do it. You aren’t entitled to the maintainer’s time, but you are, per the &lt;a href=&quot;https://opensource.org/osd&quot; target=&quot;_blank&quot;&gt;open source definition&lt;/a&gt; and &lt;a href=&quot;https://www.gnu.org/philosophy/free-sw.en.html&quot; target=&quot;_blank&quot;&gt;free software definition&lt;/a&gt;, entitled to change the software, distribute your changes to others, and to sell the software with or without those changes.&lt;/p&gt;&lt;p&gt;Though this idea is important for users of free software to understand, it’s equally important that maintainers understand this as well. We have a problem with burn-out in the free software community, wherein a maintainer, feeling pressured into accepting greater responsibility over their work from a community that increasingly depends on them, will work themselves half to death for little or no compensation. You should not do this! That wasn’t part of the deal!&lt;/p&gt;&lt;p&gt;As a maintainer, you need to be prepared to say “no”. Working on your project should never feel like a curse. You started it for a reason — remember that reason. Was it to lose your sanity? Or was it to have fun? Was it to solve a specific problem you had? Or was it to solve problems for someone you’ve never met? Remember these goals, and stay true to them. If you’re getting stressed out, stop. You can always walk away. You don’t owe anything to anyone.&lt;/p&gt;&lt;p&gt;If you enjoy the work, and you enjoy helping others, that’s great! Of course, you are allowed to help your users out if you so choose. However, I recommend that you manage their expectations, and make sure you’re spending time cultivating a healthy relationship between you, your colleagues, and your users. FOSS projects are made out of people, and maintaining that social graph is as important as maintaining the code. Make sure everyone understands the rules and talk about your frustrations with each other. Having an active dialogue can prevent problems before they happen in the first place.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Provided-as-is-without-warranty/</link>
        
        <pubDate>Mon, 14 Jun 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Provided-as-is-without-warranty/</guid>
      </item>
    
      <item>
        
        
          <title>I will be moving to the Netherlands</title>
          <description>
            &lt;p&gt;I had been planning a move to the Netherlands for a while, at least until a large COVID-shaped wrench was thrown into the gears. However, I was fully vaccinated by early April, and there are signs of the border opening up now, so my plans have been slowly getting back on track. I sent off my visa application today, and assuming I can navigate the pandemic-modified procedures, I should be able to make my move fairly soon. It’s a little bit intimidating, but I am looking forward to it!&lt;/p&gt;&lt;p&gt;&lt;em&gt;Quick note: I am looking for temporary housing in NL; somewhere I can stay for 6-12 months with a permanent mailing address for receiving immigration-related documents. I would prefer to rent out a room than to use some kind of commercial accommodation, to be certain that I can receive mail from the immigration services for the duration of the process. &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Please shoot me an email&lt;/a&gt; if you’ve got a lead! I’d rather meet someone through the FOSS community than dig through &lt;del&gt;Craigslist&lt;/del&gt; Marktplaats from overseas.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;I have felt a kind of dissonance with my home country of the United States for a long time now, and I have found it very difficult to resolve. I am not of one mind with my peers in this country on many issues; social, economic, and political. Even limiting this inquiry to matters related to FOSS, it’s quite clear that the FOSS community in Europe is much stronger than in America. In the United States, capitalism is the secular religion, and my values, in FOSS and otherwise, are incompatible with the American ethos.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Leaving the US is a selfish choice. I could stay here to get involved in solving these problems, but I chose to leave for a place which has already made much more progress on them. Ultimately, this is the only life I’m gonna get, and I have decided not to spend it on politics. I’ll spare you from the rest of the details. I’ll also acknowledge that I’m very privileged to even have this choice at all. Because I know how difficult it is to leave, for reasons unique to each person’s own situation, I don’t hold anyone who stays behind accountable for their country’s cruelties.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;So, why the Netherlands? I considered many options. For instance, I am fluent in Japanese, have an existing social network there, and understand their immigration laws and process. However, as much as I love to visit, I’m not on their cultural wavelength. Integration would pose a challenge. That said, I have also spent a lot of time in the EU, which is a hot spot for the FOSS ecosystem. Access to any EU country with a path to citizenship opens up access to the rest of the EU, making it a very strong choice with lots of second choices easily available.&lt;/p&gt;&lt;p&gt;The Netherlands is an attractive place in these respects. It is relatively easy for me to obtain a visa there, for one, but it also ranks very highly in numerous respects: social, economic, political, and basic happiness. I have many friends in Europe and I won’t have to worry too much about establishing a new social network there.&lt;/p&gt;&lt;p&gt;There are also some risks. Housing is expensive and only getting more so. Also, like the rest of the world, how NL will emerge from the crises of the pandemic remains to be seen, and many countries are likely to suffer from long-term consequences in all aspects of life. They are also already dealing with an influx of immigrants, and it’s quite possible that I will face some social and legal challenges in the future.&lt;/p&gt;&lt;p&gt;Despite these and other risks, I am optimistic about this change. The path to citizenship takes only five years, and after many careful inquiries into the process, I believe my plans for getting there are on very solid footing. I have been studying Dutch throughout much of the pandemic, and I’m not having much trouble with it — I intend to achieve fluency. Integration is well within my grasp. I expect to look back on this transition with confidence in a decision well-made.&lt;/p&gt;&lt;p&gt;Leuk jullie te ontmoeten, Nederland!&lt;/p&gt;&lt;p&gt;Oh, and yes: I will be re-locating SourceHut, the incorporated entity, to the Netherlands, and gradually moving our infrastructure over the pond. Details regarding these plans will eventually appear on the &lt;a href=&quot;https://man.sr.ht/ops&quot; target=&quot;_blank&quot;&gt;ops wiki&lt;/a&gt;. Users can expect little to no disruption from the change.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-Netherlands/</link>
        
        <pubDate>Mon, 07 Jun 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-Netherlands/</guid>
      </item>
    
      <item>
        
        
          <title>Build your project in our new language</title>
          <description>
            &lt;p&gt;Do you have a new systems programming project on your todo list? If you’re feeling adventurous, I would like you to give it a crack in our new systems programming language, and to use it to drive improvements in the less-developed areas of our standard library.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: we have enough projects on board now. Keep an eye on the blog, I’ll publish another announcement when we’re ready for more.&lt;/p&gt;&lt;p&gt;Are you making a new coreutils implementation? A little OS kernel? A new shell? A GUI toolkit? Database system? Web server? Whatever your systems programming use-case, we think that our language is likely to be a good fit for you, and your help in proving that, and spurring development to rise to meet your needs, would be quite welcome.&lt;/p&gt;&lt;p&gt;Here’s our pitch:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;XXXX is a systems programming language designed to be simple and robust. XXXX uses a static type system, manual memory management, and a minimal runtime. It is well-suited to writing operating systems, system tools, compilers, networking software, and other low-level, high performance tasks.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You can get a peek at how it feels by &lt;a href=&quot;https://drewdevault.com/2021/05/24/io_uring-finger-server.html&quot; target=&quot;_blank&quot;&gt;reading about the finger server I wrote with it&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Sounds interesting? &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Please tell me about your project idea&lt;/a&gt;!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Come-build-your-project/</link>
        
        <pubDate>Sun, 30 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Come-build-your-project/</guid>
      </item>
    
      <item>
        
        
          <title>Using io_uring to make a high-performance... finger server</title>
          <description>
            &lt;p&gt;I’m working on adding a wrapper for the &lt;a href=&quot;https://unixism.net/loti/what_is_io_uring.html&quot; target=&quot;_blank&quot;&gt;Linux io_uring interface&lt;/a&gt; to my &lt;a href=&quot;https://drewdevault.com/2021/03/19/A-new-systems-language.html&quot; target=&quot;_blank&quot;&gt;secret programming language project&lt;/a&gt;. To help learn more about io_uring and to test out the interface I was designing, I needed a small project whose design was well-suited for the value-add of io_uring. The &lt;a href=&quot;https://en.wikipedia.org/wiki/Finger_protocol&quot; target=&quot;_blank&quot;&gt;Finger protocol&lt;/a&gt; is perfect for this! After being designed in the 70’s and then completely forgotten about for 50 years, it’s the perfect small and simple network protocol to test drive this new interface with.&lt;/p&gt;&lt;p&gt;In short, finger will reach out to a remote server and ask for information about a user. It was used back in the day to find contact details like the user’s phone number, office address, email address, sometimes their favorite piece of ASCII art, and, later, a summary of the things they were working on at the moment. The somewhat provocative name allegedly comes from an older usage of the word to mean “a snitch” or a member of the FBI. The last useful RFC related to Finger is &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1288&quot; target=&quot;_blank&quot;&gt;RFC 1288&lt;/a&gt;, circa 1999, which will be our reference for this server. If you want to give it a test drive, try this to ping the server we’ll be discussing today:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;printf &amp;apos;drew\r\n&amp;apos; | nc drewdevault.com 79
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You might also have the finger command installed locally (try running “finger &lt;a href=&quot;mailto:drew@drewdevault.com&quot; target=&quot;_blank&quot;&gt;drew@drewdevault.com&lt;/a&gt;”), and you can try out the &lt;a href=&quot;https://sr.ht/~julienxx/Castor/&quot; target=&quot;_blank&quot;&gt;Castor&lt;/a&gt; browser by sourcehut user ~julienxx for a graphical experience.&lt;/p&gt;&lt;p&gt;And what is io_uring? It is the latest interface for async I/O on Linux, and it’s pretty innovative and interesting. The basic idea is to set up some memory which is shared between the kernel and the userspace program, and stash a couple of ring buffers there that can be updated with atomic writes. Userspace appends submission queue entries (SQEs) to the submission queue (SQ), and the kernel processes the I/O requests they describe and then appends completion queue events (CQEs) to the completion queue (CQ). Interestingly, both sides can see this happening &lt;em&gt;without&lt;/em&gt; entering the kernel with a syscall, which is a major performance boost. It more or less solves the async I/O problem for Linux, which Linux (and Unix at large) has struggled to do for a long time.&lt;/p&gt;&lt;p&gt;With that the background in place, I’m going to walk you through my finger server’s code. Given that this is written in an as-of-yet unreleased programming language, I’ll do my best to help you decipher the alien code.&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;A quick disclaimer&lt;/summary&gt;
  &lt;p&gt;
  This language, the standard library, and the interface provided by
  linux::io_uring, are all works in progress and are subject to change. In
  particular, this program will become obsolete when we design a portable I/O
  bus interface, which on Linux will be backed by io_uring but on other systems
  will use kqueue, poll, etc.
  &lt;/p&gt;

  &lt;p&gt;
  As a rule of thumb, anything which uses rt:: or linux:: is likely to change or
  be moved behind a portable abstraction in the future.
  &lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;Let’s start with the basics:&lt;/p&gt;&lt;!--
Yes, it&apos;s called Hare. Now keep that to yourself.
--&gt;
&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;getopt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;MAX_CLIENTS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;error keyword&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;ANY_V6&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;79u16&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;finger&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;getopt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;finger server&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;B&amp;apos;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;addr&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;address to bind to (default: all)&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;P&amp;apos;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;port to bind to (default: 79)&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;g&amp;apos;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;group&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;user group enabled for finger access (default: finger)&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;getopt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&amp;apos;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;apos; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Invalid IP address&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;P&amp;apos;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;stou16&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Invalid port&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Port exceeds range&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;g&amp;apos;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;grent&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;getgroup&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;No &amp;apos;{}&amp;apos; group available&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;gr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;grent&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;gr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;grent_finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;grent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;None of this code is related to io_uring or finger, but just handling some initialization work. This is the daemon program, and it will accept some basic configuration via the command line. The getopt configuration shown here will produce the following help string:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ fingerd -h
fingerd: finger server

Usage: ./fingerd [-B &amp;lt;addr&amp;gt;] [-P &amp;lt;port&amp;gt;] [-g &amp;lt;group&amp;gt;]

-B &amp;lt;addr&amp;gt;: address to bind to (default: all)
-P &amp;lt;port&amp;gt;: port to bind to (default: 79)
-g &amp;lt;group&amp;gt;: user group enabled for finger access (default: finger)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The basic idea is to make finger access opt-in for a given Unix account by adding them to the “finger” group. The “passwd::getgroup” lookup fetches that entry from /etc/group to identify the list of users for whom we should be serving finger access.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;serv&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		256&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;backlog&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;reuseport&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;listener&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serv&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, we set up a TCP listener. I went for a backlog of 256 connections (overkill for a finger server, but hey), and set reuseport so you can achieve CLOUD SCALE by running several daemons at once.&lt;/p&gt;&lt;p&gt;Next, I set up the io_uring that we’ll be using:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// The ring size is 2 for the accept and sigfd read, plus 2 SQEs for&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// each of up to MAX_CLIENTS: either read/write plus a timeout, or up to&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// two close SQEs during cleanup.&lt;/span&gt;
&lt;span class=&quot;error constant variable&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;MAX_CLIENTS&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;MAX_ENTRIES&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;MAX_CLIENTS&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;io_uring&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we were running this as root (and we often are, given that fingerd binds to port 79 by default), we could go syscall-free by adding &lt;code&gt;io_uring::setup_flags::SQPOLL&lt;/code&gt; to &lt;code&gt;params.flags&lt;/code&gt;, but this requires more testing on my part so I have not added it yet. With this configuration, we’ll need to use the &lt;code&gt;io_uring_enter&lt;/code&gt; syscall to submit I/O requests.&lt;/p&gt;&lt;p&gt;We also have to pick a queue size when setting up the uring. I planned this out so that we can have two SQEs in flight for every client at once — one for a read/write request and its corresponding timeout, or for the two “close” requests used when disconnecting the client — plus two extra entries, one for the “accept” call, and another to wait for signals from a signalfd.&lt;/p&gt;&lt;p&gt;Speaking of signalfds:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sigset&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sigaddset&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;SIGINT&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sigaddset&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;SIGTERM&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sigprocmask&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;SIG_BLOCK&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sigfd&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;signalfd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;signalfd&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sigfd&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;listenerfd&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;serv&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_operator&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sigfd&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;register_files&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;poll_add&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;POLLIN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;FIXED_FILE&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;set_user&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sigfd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We haven’t implemented a high-level signal interface yet, so this is just using the syscall wrappers. I chose to use a signalfd here so I can monitor for SIGINT and SIGTERM with my primary I/O event loop, to (semi-)gracefully&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; terminate the server.&lt;/p&gt;&lt;p&gt;This also happens to show off our first SQE submission. “must_get_sqe” will fetch the next SQE, asserting that there is one available, which relies on the math I explained earlier when planning for our queue size. Then, we populate this SQE with a “poll_add” operation, which polls on the first fixed file descriptor.  The “register” call above adds the socket and signal file descriptors to the io_uring’s list of “fixed” file descriptors, and so with “flags::FIXED_FILE” this refers to the signalfd.&lt;/p&gt;&lt;p&gt;We also set the user_data field of the SQE with “set_user”. This will be copied to the CQE later, and it’s necessary that we provide a unique value in order to correlate the CQE back to the SQE it refers to. We can use any value, and the address of the signalfd variable is a convenient number we can use for this purpose.&lt;/p&gt;&lt;p&gt;There’s one more step — submitting the SQE — but that’ll wait until we set up more I/O. Next, I have set up a “context” structure which will store all of the state the server needs to work with, to be passed to functions throughout the program.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;grent&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;userlist&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second “...” towards the end is not for illustrative purposes - it sets all of the remaining fields to their default values (in this case, clients becomes an empty slice).&lt;/p&gt;&lt;p&gt;Finally, this brings us to the main loop:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;accept_waiting&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peeraddr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sockaddr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;peeraddr_sz&lt;/span&gt; &lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; size&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sockaddr&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;error constant variable&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;accept_waiting&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;MAX_CLIENTS&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peeraddr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peeraddr_sz&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;FIXED_FILE&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;set_user&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peeraddr&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;accept_waiting&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cqe&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;error constant variable&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe_seen&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;get_user&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peeraddr&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peeraddr&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;accept_waiting&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sigfd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;signalfd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;siginfo&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sigfd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; size&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;signalfd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;siginfo&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;errorln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;Caught&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;terminating&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0z&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; i &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; 1&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At each iteration, assuming we have room and aren’t already waiting on a new connection, we submit an “accept” SQE to fetch the next incoming client. This SQE accepts an additional parameter to write the client’s IP address to, which we provide via a pointer to our local peeraddr variable.&lt;/p&gt;&lt;p&gt;We call “submit” at the heart of the loop to submit any SQEs we have pending (including both the signalfd poll and the accept call, but also anything our future client handling code will submit) to the io_uring, then wait the next CQE from the kernel.&lt;/p&gt;&lt;p&gt;When we get one, we defer a “cqe_seen”, which will execute at the end of the current scope (i.e. the end of this loop iteration) to advance our end of the completion queue, then figure out what I/O request was completed. The code earlier sets up SQEs for the accept and signalfd, which we check here. If a signal comes in, we read the details to acknowledge it and then terminate the loop. We also check if the user data was set to the address of any client state data, which we’ll use to dispatch for client-specific I/O later on. If a new connection comes in:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;peeraddr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sockaddr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Error: accept: {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;from_native&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peeraddr&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;MONOTONIC&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;READ_QUERY&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;deadline&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;SECOND&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;peer&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;plan_fd&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;submit_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is fairly self-explanatory, but we do see the first example of how to determine the result from a CQE. The result field of the CQE structure the kernel fills in is set to what would normally be the return value of the equivalent syscall, and “linux::io_uring::result” is a convenience function which translates negative values (i.e. errno) into a more idiomatic result type.&lt;/p&gt;&lt;p&gt;We choose a deadline here, 10 seconds from when the connection is established, for the entire exchange to be completed by. This helps to mitigate &lt;a href=&quot;https://en.wikipedia.org/wiki/Slowloris_(computer_security)&quot; target=&quot;_blank&quot;&gt;Slowloris&lt;/a&gt; attacks, though there are more mitigations we could implement for this.&lt;/p&gt;&lt;p&gt;Our client state is handled by a state machine, which starts in the “READ_QUERY” state. Per the RFC, the client will be sending us a query, followed by a CRLF. Our initial state is prepared to handle this. The full client state structure is as follows:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;READ_QUERY&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;OPEN_PLAN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;READ_PLAN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;WRITE_RESP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;WRITE_ERROR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;deadline&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;instant&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;plan_fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;plan_path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;2048&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each field will be explained in due time. We add this to our list of active connections and call “submit_read”.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;submit_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;maxread&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;maxread&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;offs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IO_LINK&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;set_user&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;timespec&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;instant_to_timespec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;deadline&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;link_timeout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;timeout_flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ABS&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ve prepared two SQEs here. The first is a read, which will fill half of the client buffer with whatever they send us over the network (why half? I’ll explain later). It’s configured with “flags::IO_LINK”, which will link it to the following request: a timeout. This will cause the I/O to be cancelled if it doesn’t complete before the deadline we set earlier. “timeout_flags::ABS” specifies that the timeout is an absolute timestamp rather than a duration computed from the time of I/O submission.&lt;/p&gt;&lt;p&gt;I set the user data to the client state pointer, which will be used the next time we have a go-around in the main event loop (feel free to scroll back up if you want to re-read that bit). The event loop will send the CQE to the dispatch function, which will choose the appropriate action based on the current client state.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;READ_QUERY&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;client_query&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;OPEN_PLAN&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;client_open_plan&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;READ_PLAN&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;client_read_plan&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;WRITE_RESP&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;WRITE_ERROR&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;client_write_resp&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;disconnect_err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;What’s the difference between match and switch? The former works with types, and switch works with values. We might attempt to merge these before the language’s release, but for now the distinction simplifies our design.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;I’ve structured the client state machine into four states based on the kind of I/O they handle, plus a special case for error handling:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Reading the query from the client&lt;/li&gt;&lt;li&gt;Opening the plan file for the requested user&lt;/li&gt;&lt;li&gt;Reading from the plan file&lt;/li&gt;&lt;li&gt;Forwarding its contents to the client&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://redacted.moe/f/2eb5650a.svg&quot;&gt;&lt;/p&gt;&lt;p&gt;Each circle in this diagram represents a point where we will submit some I/O to our io_uring instance and return to the event loop. If any I/O resulted in an error, we’ll follow the dotted line to the error path, which transmits the error to the user (and if an error occurs &lt;em&gt;during&lt;/em&gt; error transmission, we’ll immediately disconnect them, but that’s not shown here).&lt;/p&gt;&lt;p&gt;I need to give a simplified introduction to error handling in this new programming language before we move on, so let’s take a brief detour. In this language, we require the user to explicitly do &lt;em&gt;something&lt;/em&gt; about errors. Generally speaking, there are three somethings that you will do:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Some context-appropriate response to an error condition&lt;/li&gt;&lt;li&gt;Bumping the error up to the caller to deal with&lt;/li&gt;&lt;li&gt;Asserting that the error will never happen in practice&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The latter two options have special operators (”?” and “!”, respectively, used as postfix operators on expressions which can fail), and the first option is handled manually in each situation as appropriate. It’s usually most convenient to use ? to pass errors up the stack, but the buck has got to stop somewhere. In the code we’ve seen so far, we’re in or near the main function — the top of the call stack — and so have to handle these errors manually, usually by terminating the program with “!”. But, when a client causes an error, we cannot terminate the program without creating a DoS vulnerability. This “dispatch” function sets up common client error handling accordingly, allowing later functions to use the “?” operator to pass errors up to it.&lt;/p&gt;&lt;p&gt;To represent the errors themselves, we use a lightweight approach to tagged unions, similar to a result type. Each error type, optionally with some extra metadata, is enumerated, along with any possible successful types, as part of a function’s return type. The only difference between an error type and a normal type is that the former is denoted with a “!” modifier — so you can store any representable state in an error type.&lt;/p&gt;&lt;p&gt;I also wrote an “errors” file which provides uniform error handling for all of the various error conditions we can expect to occur in this program. This includes all of the error conditions that we define ourselves, as well as any errors we expect to encounter from modules we depend on. The result looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;linux&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;unexpected_eof&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;invalid_query&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;no_such_user&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;relay_denied&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;max_query&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
	&lt;span class=&quot;constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
	&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;unexpected_eof&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;invalid_query&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;no_such_user&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;relay_denied&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;
	&lt;span class=&quot;constant type variable&quot;&gt;max_query&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;unexpected_eof&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Unexpected EOF&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;invalid_query&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Invalid query&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;no_such_user&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;No such user&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;relay_denied&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Relay access denied&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;max_query&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Maximum query length exceeded&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With an understanding of error handling, we can re-read the dispatch function’s common error handling for all client issues:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;READ_QUERY&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;client_query&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;OPEN_PLAN&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;client_open_plan&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;READ_PLAN&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;client_read_plan&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;WRITE_RESP&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;WRITE_ERROR&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;client_write_resp&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;disconnect_err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each dispatched-to function returns a tagged union of (void | error), the latter being our common error type. If they return void, we do nothing, but if an error occurred, we call “disconnect_err”.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;disconnect_err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;errorfln&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;{}: Disconnecting with error: {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;unexpected_eof&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;invalid_query&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;no_such_user&lt;/span&gt;
			&lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;relay_denied&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;max_query&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;disconnect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;bsprintf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Error: {}&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;WRITE_ERROR&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;submit_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;disconnect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;plan_fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;plan_fd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We log the error here, and for certain kinds of errors, we “forward” them to the client by writing them to our client buffer and going into the “WRITE_RESP” state. For other errors, we just drop the connection.&lt;/p&gt;&lt;p&gt;The disconnect function, which disconnects the client immediately, queues io_uring submissions to close the open file descriptors associated with it, and then removes it from the list of clients.&lt;/p&gt;&lt;p&gt;Let’s get back to the happy path. Remember the read SQE we submitted when the client established the connection? When the CQE comes in, the state machine directs us into this function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;client_query&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;unexpected_eof&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;max_query&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;comment spell&quot;&gt;// The RFC requires queries to use CRLF, but it is also one of the few&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// RFCs which explicitly reminds you to, quote, &amp;quot;as with anything in the&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// IP protocol suite, &amp;apos;be liberal in what you accept&amp;apos;&amp;quot;, so we accept LF&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// as well.&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lf&lt;/span&gt; &lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error character string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; z&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;max_query&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;submit_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;character string_escape&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;lf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// CRLF&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;try_fromutf8&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;invalid_query&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;printfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;{}: finger {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;plan&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;process_query&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;plan_path&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;to_c&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;openat&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;AT_FDCWD&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;plan_path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;O_RDONLY&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;set_user&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;OPEN_PLAN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first half of this function figures out if we’ve received a full line, including CRLF. The second half parses this line as a finger query and prepares to fulfill the enclosed request.&lt;/p&gt;&lt;p&gt;The read operation behaves like the read(2) syscall, which returns 0 on EOF. We aren’t expecting an EOF in this state, so if we see this, we boot them out. We also have a cap on our buffer length, so we return the max_query error if it’s been exceeded. Otherwise, we look for a line feed. If there isn’t one, we submit another read to get more from the client, but if a line feed is there, we trim off a carriage return (if present) and decode the completed query as a UTF-8 string.&lt;/p&gt;&lt;p&gt;We call “process_query” (using the error propagation operator to bubble up errors), which returns the path to the requested user’s ~/.plan file. We’ll look at the guts of that function in a moment. The return value is heap allocated, so we defer a free for later.&lt;/p&gt;&lt;p&gt;Strings in our language are not null terminated, but io_uring expects them to be. This is another case which will be addressed transparently once we build a higher-level, portable interface. For now, though, we need to call “strings::to_c” ourselves, and stash it on the client struct. It’s heap allocated, so we’ll free it in the next state when the I/O submission completes.&lt;/p&gt;&lt;p&gt;Speaking of which, we finish this process after preparing the next I/O operation — opening the plan file — and setting the client state to the next step in the state machine.&lt;/p&gt;&lt;p&gt;Before we move on, though, I promised that we’d talk about the process_query function. Here it is in all of its crappy glory:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;unix&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;process_query&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;has_prefix&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;/W&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;has_prefix&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;/w&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;has_prefix&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;has_prefix&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;@&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;relay_denied&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pwent&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;getuser&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;no_such_user&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;pwent&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;pwent_finish&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;pwent&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;no_such_user&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pwent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;homedir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;.plan&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1288#section-2.3&quot; target=&quot;_blank&quot;&gt;grammar described in RFC 1288&lt;/a&gt; is pretty confusing, but most of it is to support features I’m not interested in for this simple implementation, like relaying to other finger hosts or requesting additional information. I think I’ve “parsed” most of the useful bits here, and ultimately I’m aiming to end up with a single string: the username whose details we want. I grab the user’s passwd entry and check if they’re a member of the “finger” group we populated way up there in the first code sample. If so, we pull the path to their homedir out of the passwd entry, join it with “.plan”, and send it up the chain.&lt;/p&gt;&lt;p&gt;At this point we’ve received, validated, and parsed the client’s query, and looked up the plan file we need. The next step is to open the plan file, which is where we left off at the end of the last function. The I/O we prepared there takes us here when it completes:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;client_open_plan&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;plan_path&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;plan_fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;READ_PLAN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;submit_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;plan_fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By now, this should be pretty comprehensible. I will clarify what the “[..0]” syntax does here, though. This language has slices, which store a pointer to an array, a length, and a capacity. In our client state, xbuf is a fixed-length array which provides the actual storage, and “buf” is a slice of that array, which acts as a kind of cursor, telling us what portion of the buffer is valid. The result of this expression is to take a slice up to, but not including, the 0th item of that array — in other words, an empty slice. The address and capacity of the slice still reflect the traits of the underlying array, however, which is what we want.&lt;/p&gt;&lt;p&gt;We’re now ready to read data out of the user’s plan file. We submit a read operation for that file descriptor, and when it completes, we’ll end up here:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client_read_plan&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;disconnect&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// Convert LF to CRLF&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// We always read a maximum of the length of xbuf over two so that we&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// have room to insert these.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;seencrlf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error character string_escape&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;seencrlf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error character string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;seencrlf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error character string_escape&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
				&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;seencrlf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;WRITE_RESP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;submit_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, the read operation for io_uring behaves similarly to the read(2) syscall, so it returns the number of bytes read. If this is zero, or EOF, we can terminate the state machine and disconnect the client (this is a nominal disconnect, so we don’t use disconnect_err here). If it’s nonzero, we set our buffer slice to the subset of the buffer which represents the data io_uring has read.&lt;/p&gt;&lt;p&gt;The Finger RFC requires all data to use CRLF for line endings, and this is where we deal with it. Remember earlier when I noted that we only ever used half of the read buffer? This is why: if we read 1024 newlines from the plan file, we will need another 1024 bytes to insert carriage returns. Because we’ve planned for and measured out our memory requirements in advance, we can use “static insert” here. This built-in works similarly to how insert normally works, but it will never re-allocate the underlying array. Instead, it asserts that the insertion would not require a re-allocation, and if it turns out that you did the math wrong, it aborts the program instead of buffer overflowing. But, we did the math and it works out, so it saves us from an extra allocation.&lt;/p&gt;&lt;p&gt;Capping this off, we submit a write to transmit this buffer to the client. “submit_write” is quite similar to submit_read:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;submit_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;IO_LINK&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;set_user&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;rt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;timespec&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;instant_to_timespec&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;deadline&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;must_get_sqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;uring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;link_timeout&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;timeout_flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ABS&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ideally, this should not require explanation. From here we transition to the WRITE_RESP state, so when the I/O completes we end up here:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;client_write_resp&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;submit_write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;WRITE_ERROR&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;disconnect&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;xbuf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;READ_PLAN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;submit_read&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;plan_fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we check if we need to repeat this process: if we have written less than the size of the buffer, then we advance the slice by that much and submit another write.&lt;/p&gt;&lt;p&gt;We can arrive at the next bit for two reasons: because “client.buf” includes a fragment of a plan file which has been transmitted to the client, which we just covered, or because it is the error message buffer prepared by “disconnect_err”, which we discussed earlier. The dispatch function will bring us here for both the normal and error states, and we distinguish between them with this second if statement. If we’re sending the plan file, we submit a read for the next buffer-ful of plan. But, our error messages always fit into one buffer, so if we ran out of buffer then we can just disconnect in the error case.&lt;/p&gt;&lt;p&gt;And that’s it! That completes our state machine, and I’m pretty sure we’ve read the entire program’s source code by this point. Pretty neat, huh? io_uring is quite interesting. I plan on using this as a little platform upon which I can further test our io_uring implementation and develop a portable async I/O abstraction.  We haven’t implemented a DNS resolver for the stdlib yet, but I’ll also be writing a finger client (using synchronous I/O this time) once we do.&lt;/p&gt;&lt;p&gt;If you really wanted to max out the performance for a CLOUD SCALE WEB 8.0 XTREME PERFORMANCE finger server, we could try a few additional improvements:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Adding an internal queue for clients until we have room for their I/O in the SQ&lt;/li&gt;&lt;li&gt;Using a shared buffer pool with the kernel, with io_uring ops like READ_FIXED&lt;/li&gt;&lt;li&gt;Batching requests for the same plan file by only answering requests for it every Nth millisecond (known to some as the “data loader” pattern)&lt;/li&gt;&lt;li&gt;More slow loris mitigations, such as limiting open connections per IP address&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It would also be cool to handle SIGHUP to reload our finger group membership list without rebooting the daemon. I would say “patches welcome”, but I won’t share the git repo until the language is ready. And the code is GPL’d, but not AGPL’d, so you aren’t entitled to it if you finger me!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/io_uring-finger-server/</link>
        
        <pubDate>Mon, 24 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/io_uring-finger-server/</guid>
      </item>
    
      <item>
        
        
          <title>How to write release notes</title>
          <description>
            &lt;p&gt;Release notes are a concept most of us are familiar with. When a new software release is prepared, the release notes tell you what changed, so you understand what you can expect and how to prepare for the update. They are also occasionally used to facilitate conversations:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/2010/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/update_notes_2x.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Many of the people tasked with writing release notes have never found themselves on that side of the screen before. If that describes you, I would like to offer some advice on how to nail it. Note that this mostly applies to free and open source software, which is the only kind of software which is valid.&lt;/p&gt;&lt;p&gt;So, it’s release day, and you’re excited about all of the cool new features you’ve added in this release. I know the feeling! Your first order of business, however, is to direct that excitement into the blog or mailing list post announcing the release, rather than into the release notes. When I read the release notes, the first thing I need answered is: “what do I need to do when I upgrade?” You should summarize the breaking changes upfront, and what steps the user will need to take in order to address them. After this, you may follow up with a &lt;em&gt;short&lt;/em&gt; list of the flagship improvements which are included in this release. Keep it short — remember that we’re not advertising the release, but facilitating the user’s upgrade. This is a clerical document.&lt;/p&gt;&lt;p&gt;That said, you do have a good opportunity to add a &lt;em&gt;small&lt;/em&gt; amount of faffery after this. Some projects say “$project version $X includes $Y changes from $Z contributors”. The detailed changelog should follow, including every change which shipped in the release. This is what users are going to scan to see if that one bug which has been bothering them was addressed in this version. If you have &lt;a href=&quot;https://drewdevault.com/2019/02/25/Using-git-with-discipline.html&quot; target=&quot;_blank&quot;&gt;good git discipline&lt;/a&gt;, you can take advantage of &lt;a href=&quot;https://git-scm.com/docs/git-shortlog&quot; target=&quot;_blank&quot;&gt;git shortlog&lt;/a&gt; to automatically generate a summary of the changes.&lt;/p&gt;&lt;p&gt;Once you’ve prepared this document, where should you put it? In my opinion, there’s only one appropriate place for it: an annotated git tag. I don’t like “CHANGELOG” files and I definitely don’t like GitHub releases. If you add “-a” to your “git tag” command, git will fire up an editor and you can fill in your changelog just like you write your git commit messages. This associates your changelog with the git data it describes, and automatically distributes it to all users of the git repository. Most web services which host git repositories will display it on their UI as well. It’s also written in plaintext, which conveniently prevents you from being too extra with your release notes — no images or videos or such.&lt;/p&gt;&lt;p&gt;I have written a small tool which will make all of this easier for you to do: “&lt;a href=&quot;https://git.sr.ht/~sircmpwn/dotfiles/tree/master/bin/semver&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt;”. This automatically determines the next release number, optionally runs a custom script to automate any release bookkeeping you need to do (e.g. updating the version in your Makefile), then generates the git shortlog and plops you into an editor to flesh out the release notes. I wrote more about this tool in &lt;a href=&quot;https://drewdevault.com/2019/10/12/how-to-fuck-up-releases.html&quot; target=&quot;_blank&quot;&gt;How to fuck up software releases&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I hope this advice helps you improve your release notes! Happy shipping.&lt;/p&gt;&lt;p&gt;P.S. Here’s an example of a changelog which follows this advice:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;wlroots 0.12.0 includes the following breaking changes:

# New release key

The PGP key used to sign this release has changed to
34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48. A proof of legitimacy signed with the
previous key is available here:

https://github.com/swaywm/wlroots/issues/2462#issuecomment-723578521

# render/gles2: remove gles2_procs global (#2351)

The wlr_gles2_texture_from_* family of functions are no longer public API.

# output: fix blurred hw cursors with fractional scaling (#2107)

For backends: wlr_output_impl.set_cursor now takes a float &amp;quot;scale&amp;quot; instead of an
int32_t.

# Introduce wlr_output_event_commit (#2315)

The wlr_output.events.commit event now has a data argument of type
struct wlr_output_event_commit * instead of struct wlr_output *.


Antonin Décimo (3):
      Fix typos
      Fix incorrect format parameters
      xwayland: free server in error path

Isaac Freund (6):
      xdg-shell: split last-acked and current state
      layer-shell: add for_each_popup
      layer-shell: error on 0 dimension without anchors
      xdg_positioner: remove unused field
      wlr_drag: remove unused point_destroy field
      xwayland: remove unused listener

Roman Gilg (3):
      output-management-v1: add head identifying events
      output-management-v1: send head identifying information
      output-management-v1: send complete head state on enable change

Ryan Walklin (4):
      Implement logind session SetType method to change session type to wayland
      Also set XDG_SESSION_TYPE
      Don&amp;apos;t set XDG_SESSION_TYPE unless logind SetType succeeds
      Quieten failure to set login session type

Scott Moreau (2):
      xwm: Set _NET_WM_STATE_FOCUSED property for the focused surface
      foreign toplevel: Fix whitespace error
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Note: I borrowed the real wlroots 0.12.0 release notes and trimmed them down for illustrative purposes. The actual release included a lot more changes and does not actually follow all of my recommendations.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-write-release-notes/</link>
        
        <pubDate>Wed, 19 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-write-release-notes/</guid>
      </item>
    
      <item>
        
        
          <title>aerc, mbsync, and postfix for maximum comfy offline email</title>
          <description>
            &lt;p&gt;I am the original author of the &lt;a href=&quot;https://aerc-mail.org&quot; target=&quot;_blank&quot;&gt;aerc mail client&lt;/a&gt;, though my official relationship with it today is marginal at best. I think that, with hindsight, I’ve come to understand that the “always online” approach of aerc’s IMAP implementation is less than ideal. The next email client (which will exist at some point!) will improve on this design, but, since it’s still my favorite email client despite these flaws, they will have to be worked around.&lt;/p&gt;&lt;p&gt;To this end, I have updated my personal aerc setup to take advantage of its &lt;a href=&quot;https://en.wikipedia.org/wiki/Maildir&quot; target=&quot;_blank&quot;&gt;Maildir&lt;/a&gt; support instead of having it use IMAP directly, then delegate IMAP to &lt;a href=&quot;https://isync.sourceforge.io&quot; target=&quot;_blank&quot;&gt;mbsync&lt;/a&gt;. This brings a much-needed level of robustness to the setup, as my Maildirs are available offline or on a flaky connection, and postfix will handle queueing and redelivery of outgoing emails in similar conditions.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This allows me to read and reply to email entirely offline, and have things sync up automatically when a connection becomes available.&lt;/p&gt;&lt;p&gt;The mbsync configuration format is kind of weird, but it is pretty flexible. My config file ended up looking like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;IMAPAccount migadu
Host imap.migadu.com
User drew@ddevault.org
Pass [...]
SSLType IMAPS

MaildirStore local
Path ~/mail/
INBOX ~/mail/INBOX
SubFolders Verbatim

IMAPStore migadu
Account migadu

Channel primary
Far :migadu:
Near :local:
Patterns INBOX Archive Sent Junk
Expunge Both
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The password can be configured to run an external command if you prefer to integrate this with your keyring or password manager. I updated my aerc &lt;code&gt;accounts.conf&lt;/code&gt; as well, which was straightforward:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[Drew]
source = maildir://~/mail
outgoing = /usr/sbin/sendmail
from = Drew DeVault &amp;lt;drew@ddevault.org&amp;gt;
copy-to = Sent
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running &lt;code&gt;mbsync primary&lt;/code&gt; at this point is enough to fetch these mailboxes from IMAP and populate the local Maildirs, which can then be read with aerc. I set up a simple cronjob to run this every minute to keep it up to date:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;* * * * * chronic mbsync primary
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;chronic is a small utility from &lt;a href=&quot;https://joeyh.name/code/moreutils/&quot; target=&quot;_blank&quot;&gt;moreutils&lt;/a&gt; which converts reasonably behaved programs that return a nonzero exit status into the back-asswards behavior cron expects, which is that printing text to stdout means an error occurred and any status code, successful or not, is disregarded. You might want to tweak this further, perhaps by just directing all output into /dev/null instead, if you don’t want failed syncs to fill up your Unix mail spool.&lt;/p&gt;&lt;p&gt;mbsync is bidirectional (it is recommended to leave &lt;code&gt;Expunge both&lt;/code&gt; out of your config until you’ve tested the setup), so deleting or archiving emails in aerc will mirror the changes in IMAP as well.&lt;/p&gt;&lt;p&gt;Postfix is a lot more annoying to configure. You should assume that what I did here isn’t going to work for you without additional changes and troubleshooting. I started with an &lt;code&gt;/etc/postfix/sasl_passwd&lt;/code&gt; file like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[smtp.migadu.com]:465   drew@ddevault.org:password
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The usual &lt;code&gt;postmap /etc/postfix/sasl_passwd&lt;/code&gt; applies here to create or update the database file. Then I moved on to &lt;code&gt;main.cf&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# Allows localhost to relay mail
mynetworks = 127.0.0.0/8

# SMTP server to relay mail through
relayhost = [smtp.migadu.com]:465

# Auth options for SMTP relay
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd

# ¯\_(ツ)_/¯
smtp_tls_security_level = encrypt
smtp_tls_wrappermode = yes
smtp_use_tls = yes
smtp_sasl_security_options = 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Good luck!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Updated 2021-05-25&lt;/strong&gt;: isync is not a fork of mbsync.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/aerc-with-mbsync-postfix/</link>
        
        <pubDate>Mon, 17 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/aerc-with-mbsync-postfix/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, May 2021</title>
          <description>
            &lt;p&gt;Hello! This update is a bit late. I was travelling all day yesterday without internet, so I could not prepare these. After my sister and I got vaccinated, I took a trip to visit her at her home in beautiful Hawaii — it felt great after a year of being trapped within these same four walls. I hope you get that vaccine and things start to improve for you, too!&lt;/p&gt;&lt;p&gt;In SourceHut news, I’ve completed and shipped the first version of the builds.sr.ht GraphQL API. Another update, implementing the write functionality, will be shipping shortly, once the code review is complete. The next one up for a GraphQL API will probably be lists.sr.ht. After that it’s just man.sr.ht, paste.sr.ht, and dispatch.sr.ht — all three of which are pretty small. Then we’ll implement a few extra features like GraphQL-native webhooks and we’ll be done!&lt;/p&gt;&lt;p&gt;Adnan Maolood has also been hard at work improving &lt;a href=&quot;https://godocs.io&quot; target=&quot;_blank&quot;&gt;godocs.io&lt;/a&gt;, including the now-available &lt;a href=&quot;gemini://godocs.io&quot; target=&quot;_blank&quot;&gt;gemini version&lt;/a&gt;. I wrote a post just about godocs.io &lt;a href=&quot;https://drewdevault.com/2021/05/07/godocs.io-six-months-later.html&quot; target=&quot;_blank&quot;&gt;earlier this month&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Here’s some secret project code I’ve been working on recently:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;linux&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;setup_flags&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;linux&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ring&lt;/span&gt; &lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;get_sqe&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;sqe&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;toutf8&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;Hello world!&lt;/span&gt;&lt;span class=&quot;string string_escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;submit_wait&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;get_cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;opaque&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Error: {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;error type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;cqe&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
                        &lt;span class=&quot;constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io_uring&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;errorfln&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;result: {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cqe&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constant field variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The API here is a bit of a WIP, and it won’t be available to users, anyway — the low-level io_uring API will be wrapped by a portable event loop interface (tentatively named “iobus”) in the standard library. I’m planning on using this to write a &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1288&quot; target=&quot;_blank&quot;&gt;finger&lt;/a&gt; server.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-May-2021/</link>
        
        <pubDate>Sun, 16 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-May-2021/</guid>
      </item>
    
      <item>
        
        
          <title>Pinebook Pro review</title>
          <description>
            &lt;p&gt;I received the original Pinebook for free from the good folks at Pine64 a few years ago, when I visited Berlin to work with the KDE developers. Honestly, I was underwhelmed. The performance was abysmal and ARM is a nightmare to work with. For these reasons, I was skeptical when I bought the Pinebook Pro. I have also &lt;a href=&quot;https://drewdevault.com/2020/02/18/Fucking-laptops.html&quot; target=&quot;_blank&quot;&gt;spoken of my disdain for modern laptops in general before&lt;/a&gt;: the state of laptops in $CURRENTYEAR is abysmal. As such, I have been using a ThinkPad X200, an 11 year old laptop, as my sole laptop for several years now.&lt;/p&gt;&lt;p&gt;I am pleased to share that the Pinebook Pro is a pleasure to use, and is likely to finally replace the old ThinkPad for most of my needs.&lt;/p&gt;&lt;p&gt;Let me get the bad parts out of the way upfront: ARM is still a nightmare to work with. I really hate this architecture. Alpine Linux’s upstream aarch64 doesn’t work with this laptop, so I have to use &lt;a href=&quot;http://postmarketos.org&quot; target=&quot;_blank&quot;&gt;postmarketOS&lt;/a&gt;, an Alpine derivative, instead. I do &lt;em&gt;like&lt;/em&gt; pmOS — on phones — but I would definitely prefer to use Alpine upstream for a laptop use-case.  That being said, the Pine community has been doing a very good job of working on getting support for their devices upstream, and the situation has been steadily improving. I expect that one of the next batches of PBPs will include an updated u-Boot payload which will make UEFI booting possible, and Linux distros with the necessary kernel patches upstreamed will be shipping in the foreseeable future. This will alleviate most of my ARM-based grievances.&lt;/p&gt;&lt;p&gt;The built-in speakers are also pretty tinny and weak. It has a headphone port which works fine, though. Configuring ALSA is a chore; these SoCs tend to have rather complicated audio setups. I have not been able to get the webcam working (some kernel option is missing, my contact at pmOS is working on it), but I understand that the quality is pretty poor. It can supposedly be configured to work with a USB-C dock for an external display, but I have never got it working and I understand that there are some kernel bits missing for this as well. The touchpad is also pretty bad, but thankfully I use mainly keyboard-driven software. The built-in eMMC storage is pretty small, though it can be upgraded and I understand that there is an option to install an NVMe — at the expense of your battery life.&lt;/p&gt;&lt;p&gt;Cons aside, what do I like about it? Well, many things. It’s lightweight and thin (1.3kg), but has a nice 14” screen that feels like the right size for me. The screen looks really nice, too. The colors look good, it works well at any brightness level, and in most lighting situations. It’s definitely better than the old X200 display. The keyboard is not as nice as the ThinkPad (a high bar to meet), but it’s pretty comfortable for extended use. The two USB-3 ports and the sole USB-C port are also nice to have. It can charge via USB-C, or via an included DC wall wart and barrel plug. The battery lasts for 6-8 hours: way better than my old ThinkPad.&lt;/p&gt;&lt;p&gt;It is an ARM machine, so the performance is not competitive with modern x86_64 platforms. It is somewhat faster than my 11-year-old previous machine, though. It has six cores and any parallelizable job (like building code) works acceptably fast, at least for the languages I primarily use (i.e. not Rust or C++). It can also play back 1080p video with a &lt;em&gt;little bit&lt;/em&gt; of stuttering, and 720p video flawlessly. Browsing the web is a bit of a chore, but it always was. &lt;a href=&quot;https://sourcehut.org/blog/2021-05-08-sourcehut-is-the-fastest-who-cares&quot; target=&quot;_blank&quot;&gt;Sourcehut works fine&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The device is user-servicable, which I appreciate very much. It’s very easy to take apart (a small Phillips head screwdriver is sufficient) and you can buy individual parts from the Pine64 store to do replacements yourself.&lt;/p&gt;&lt;p&gt;In short, it checks most of my boxes, which is something no other laptop has even come remotely close to in the past &lt;strong&gt;ten years&lt;/strong&gt;. It is the only laptop I have ever used which makes a substantial improvement on the circa-2010 state of the art. Because ARM is a nightmare, I’m still likely to use the old ThinkPads for some use-cases, namely for hobby OS development and running niche operating systems. But my Pinebook Pro is here to stay.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Pinebook-Pro-review/</link>
        
        <pubDate>Fri, 14 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Pinebook-Pro-review/</guid>
      </item>
    
      <item>
        
        
          <title>I try not to make unlikable software (and features)</title>
          <description>
            &lt;p&gt;I am writing to you from The Sky. On my flight today, I noticed an example of “unlikable” software — something I’ve been increasingly aware of recently — inspiring me to pull out my laptop and write. On this plane, there are displays in the back of each seat which provides entertainment for the person seated one row back. Newer planes no longer include these, given that in $CURRENTYEAR everyone would just prefer some power for their phone or laptop. Nevertheless, you can still end up a plane with this design. You can shut the thing off by repeatedly pressing the “☀️ -” button, though that button is rated for half the cycles it will have already received by the time you press it.&lt;/p&gt;&lt;p&gt;When the flight safety video is playing, or an announcement is being made, however, the system will override your brightness preference. This is a fairly reasonable design choice, added in the name of passenger safety. What’s less reasonable is that the same feature is re-purposed for shoving advertising into your face a few minutes later. In fact, it spends more time on ads than on safety. A software engineer sat down and deliberately wrote a “feature” (or anti-feature?) which they had to have known that the user would not have wanted. The airplane manufacturer demanded it at the &lt;em&gt;expense&lt;/em&gt; of the user.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;I have had many opportunities throughout my career to make similar anti-features, and I have encountered many other examples of this behavior in the wild. Many programmers have implemented something which measurably &lt;em&gt;worsens&lt;/em&gt; the experience for the user in order to obtain some perceived benefit for the company they work for. &lt;a href=&quot;https://www.darkpatterns.org&quot; target=&quot;_blank&quot;&gt;Dark patterns&lt;/a&gt; provides many additional examples, but this kind of thing is &lt;em&gt;everywhere&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I find this behavior to be incredibly disrespectful to the user. When I am that user who is being disrespected, I will generally stop using that software, and stop supporting any businesses who chose to be disrespectful.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; For my part as a programmer, I &lt;em&gt;do&lt;/em&gt; respect the user, I find satisfaction in making software which makes their lives better, and I always have and always will push back against anyone who demands that I subvert that ethos for their wallet’s sake. You should always aim to make the user’s experience more pleasant, not more unpleasant. We should just be nice to people. That’s it: please be nice to people. Thank you for coming to my Ted talk.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Try-not-to-make-unlikable-software/</link>
        
        <pubDate>Sat, 08 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Try-not-to-make-unlikable-software/</guid>
      </item>
    
      <item>
        
        
          <title>godocs.io six months later</title>
          <description>
            &lt;p&gt;We’re six months on from &lt;a href=&quot;https://drewdevault.com/2020/12/18/godocs.io.html&quot; target=&quot;_blank&quot;&gt;forking godoc.org&lt;/a&gt; following its upstream deprecation, and we’ve made a lot of great improvements since. For those unaware, the original godoc.org was replaced with pkg.go.dev, and a redirect was set up. The new website isn’t right for many projects — one of the most glaring issues is the narrow list of software licenses pkg.go.dev will display documentation for. To continue serving the needs of projects which preferred the old website, we forked the project and set up &lt;a href=&quot;https://godocs.io&quot; target=&quot;_blank&quot;&gt;godocs.io&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Since then, we’ve made a lot of improvements, both for the hosted version and for the &lt;a href=&quot;https://sr.ht/~sircmpwn/godocs.io/&quot; target=&quot;_blank&quot;&gt;open source project&lt;/a&gt;. Special thanks is due to Adnan Maolood, who has taken charge of a lot of these improvements, and also to a few other contributors who have helped in their own small ways. Since forking, we’ve:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Added Go modules support&lt;/li&gt;&lt;li&gt;Implemented &lt;a href=&quot;gemini://godocs.io&quot; target=&quot;_blank&quot;&gt;Gemini access&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Made most of the frontend JavaScript optional and simpler&lt;/li&gt;&lt;li&gt;Rewritten the search backend to use PostgreSQL&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We also substantially cleaned up the codebase, removing over 37,000 lines of code — 64% of the lines from the original code base. The third-party dependencies to Google infrastructure have been removed and it’s much easier to run the software locally or on your intranet, too.&lt;/p&gt;&lt;p&gt;What we have now is still the same GoDoc: the experience is very similar to the original godocs.org. However, we have substantially improved it: streamlining the codebase, making the UI more accessible, and adding a few important features; thanks to the efforts of just a small number of volunteers. We’re happy to be supporting the Go community with this tool, and looking forward to making more (conservative!) improvements in the future. Enjoy!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/godocs.io-six-months-later/</link>
        
        <pubDate>Fri, 07 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/godocs.io-six-months-later/</guid>
      </item>
    
      <item>
        
        
          <title>In praise of Alpine Linux</title>
          <description>
            &lt;p&gt;&lt;em&gt;Note: this blog post was originally only available via Gemini, but has been re-formatted for the web.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The traits I prize most in an operating system are the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Simplicity&lt;/li&gt;&lt;li&gt;Stability&lt;/li&gt;&lt;li&gt;Reliability&lt;/li&gt;&lt;li&gt;Robustness&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;As a bonus, I’d also like to have:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Documentation&lt;/li&gt;&lt;li&gt;Professionalism&lt;/li&gt;&lt;li&gt;Performance&lt;/li&gt;&lt;li&gt;Access to up-to-date software&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Alpine meets all of the essential criteria and most of the optional criteria (documentation is the weakest link), and far better than any other Linux distribution.&lt;/p&gt;&lt;p&gt;In terms of simplicity, Alpine Linux is unpeered. Alpine is the only Linux distribution that fits in my head. The pieces from which it is built from are simple, easily understood, and few in number, and I can usually predict how it will behave in production. The software choices, such as musl libc, are highly appreciated in this respect as well, lending a greater degree of simplicity to the system as a whole.&lt;/p&gt;&lt;p&gt;Alpine also meets expectations in terms of stability, though it is not alone in this respect. Active development is done in an “edge” branch, which is what I run on my main workstation and laptops. Every six months, a stable release is cut from this branch and supported for two years, so four releases are supported at any given moment. This strikes an excellent balance: two years is long enough that the system is stable and predictable for a long time, but short enough to discourage you from letting the system atrophy. An outdated system is not a robust system.&lt;/p&gt;&lt;p&gt;In terms of reliability, I can be confident that an Alpine system will work properly for an extended period of time, without frequent hands-on maintenance or problem solving. Upgrading between releases almost always goes off without a hitch (and usually the hitch was documented in the release notes, if you cared to read them), and I’ve never had an issue with patch releases. Edge is less reliable, but only marginally: it’s much more stable than, say, Arch Linux.&lt;/p&gt;&lt;p&gt;The last of my prized traits is robustness, and Alpine meets this as well. The package manager, apk, is seriously robust. It expresses your constraints, and the constraints of your desired software, and solves for a system state which is always correct and consistent. Alpine’s behavior under pathological conditions is generally predictable and easily understood. OpenRC is not as good, but thankfully it’s slated to be replaced in the foreseeable future.&lt;/p&gt;&lt;p&gt;In these respects, Alpine is unmatched, and I would never dream of using any other Linux distribution in production.&lt;/p&gt;&lt;p&gt;Documentation is one of Alpine’s weak points. This is generally offset by Alpine’s simplicity — it can usually be understood reasonably quickly and easily even in the absence of documentation — but it remains an issue. That being said, Alpine has shown consistent progress in this respect in the past few releases, shipping more manual pages, improving the wiki, and standardizing processes for matters like release notes.&lt;/p&gt;&lt;p&gt;I also mostly appreciate Alpine’s professionalism. It is a serious project and almost everyone works with the level of professionalism I would expect from a production operating system. However, Alpine lacks strong leadership, some trolling and uncooperative participants go unchecked, and political infighting has occurred on a few occasions. This is usually not an impedance to getting work done, but it is frustrating nevertheless. I always aim to work closely with upstream on any of the projects that I use, and a professional upstream team is a luxury that I very much appreciate when I can find it.&lt;/p&gt;&lt;p&gt;Alpine excels in my last two criteria: performance and access to up-to-date software. apk is simply the fastest package manager available. It leaves apt and dnf in the dust, and is significantly faster than pacman. Edge updates pretty fast, and as a package maintainer it’s usually quite easy to get new versions of upstream software in place quickly even for someone else’s package. I can expect upstream releases to be available on edge within a few days, if not a few hours. Access to new software in stable releases is reasonably fast, too, with less than a six month wait for systems which are tracking the latest stable Alpine release.&lt;/p&gt;&lt;p&gt;In summary, I use Alpine Linux for all of my use-cases: dedicated servers and virtual machines in production, on my desktop workstation, on all of my laptops, and on my PinePhone (via postmarketOS). It is the best Linux distribution I have used to date. I maintain just under a hundred Alpine packages upstream, three third-party package repositories, and several dozens of Alpine systems in production. I highly recommend it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Praise-for-Alpine-Linux/</link>
        
        <pubDate>Thu, 06 May 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Praise-for-Alpine-Linux/</guid>
      </item>
    
      <item>
        
        
          <title>Cryptocurrency is an abject disaster</title>
          <description>
            &lt;p&gt;This post is long overdue. Let’s get it over with.&lt;/p&gt;&lt;div class=&quot;alert alert-danger&quot;&gt;
  🛑 &lt;strong&gt;Hey!&lt;/strong&gt; If you write a comment about this article online,
  disclose your stake in cryptocurrency. I will explain why later in this post.
  For my part, I held &amp;lt;$10,000 USD worth of Bitcoin prior to 2016, plus small
  amounts of altcoins. I made a modest profit on my holdings. Today my stake in
  all cryptocurrency is $0.
&lt;/div&gt;
&lt;p&gt;Starting on May 1st, users of sourcehut’s CI service will be required to be on a paid account, a change which will affect about half of all builds.sr.ht users.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Over the past several months, everyone in the industry who provides any kind of free CPU resources has been dealing with a massive outbreak of abuse for cryptocurrency mining. The industry has been setting up informal working groups to pool knowledge of mitigations, communicate when our platforms are being leveraged against one another, and cumulatively wasting thousands of hours of engineering time implementing measures to deal with this abuse, and responding as attackers find new ways to circumvent them.&lt;/p&gt;&lt;p&gt;Cryptocurrency has invented an entirely new category of internet abuse. CI services like mine are not alone in this struggle: JavaScript miners, botnets, and all kinds of other illicit cycles are being spent solving pointless math problems to make money for bad actors. Some might argue that abuse is inevitable for anyone who provides a public service — but prior to cryptocurrency, what kind of abuse would a CI platform endure? Email spam? Block port 25. Someone might try to host their website on ephemeral VMs with dynamic DNS or something, I dunno. Someone found a way of monetizing stolen CPU cycles directly, so everyone who offered free CPU cycles for legitimate use-cases is now unable to provide those services. If not for cryptocurrency, these services would still be available.&lt;/p&gt;&lt;p&gt;Don’t make the mistake of thinking that these are a bunch of script kiddies. There are large, talented teams of engineers across several organizations working together to combat this abuse, and they’re losing. A small sample of tactics I’ve seen or heard of include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Using CPU limiters to manipulate monitoring tools.&lt;/li&gt;&lt;li&gt;Installing crypto miners into the build systems for free software projects so that the builds appear legitimate.&lt;/li&gt;&lt;li&gt;Using password dumps to steal login credentials for legitimate users and then leveraging their accounts for mining.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I would give more examples, but secrecy is a necessary part of defending against this — which really sucks for an organization that otherwise strives to be as open and transparent as sourcehut does.&lt;/p&gt;&lt;p&gt;Cryptocurrency problems are more subtle than outright abuse, too. The integrity and trust of the entire software industry has sharply declined due to cryptocurrency. It sets up perverse incentives for new projects, where developers are no longer trying to convince you to use their software because it’s good, but because they think that if they can convince you it will make them rich. I’ve had to develop a special radar for reading product pages now: a mounting feeling of dread as a promising technology is introduced while I inevitably arrive at the buried lede: it’s more crypto bullshit. Cryptocurrency is the multi-level marketing of the tech world. “Hi! How’ve you been? Long time no see! Oh, I’ve been working on this cool distributed database file store archive thing. We’re doing an ICO next week.” Then I leave. Any technology which is not an (alleged) currency and which incorporates blockchain anyway would always work better without it.&lt;/p&gt;&lt;p&gt;There are hundreds, perhaps thousands, of cryptocurrency scams and ponzi schemes trussed up to look like some kind of legitimate offering. Even if the project &lt;em&gt;you’re&lt;/em&gt; working on is totally cool and solves all of these problems, there are 100 other projects pretending to be like yours which are ultimately concerned with transferring money from their users to their founders. Which one are investors more likely to invest in? Hint: it’s the one that’s more profitable. Those promises of “we’re different!” are always hollow anyway. Remember the &lt;a href=&quot;https://en.wikipedia.org/wiki/The_DAO_(organization)&quot; target=&quot;_blank&quot;&gt;DAO&lt;/a&gt;? They wanted to avoid social arbitration entirely for financial contracts, but when the chips are down and their money was walking out the door, they forked the blockchain.&lt;/p&gt;&lt;p&gt;That’s what cryptocurrency is all about: not novel technology, not empowerment, but making money. It has failed as an actual &lt;em&gt;currency&lt;/em&gt; outside of some isolated examples of failed national economies. No, cryptocurrency is not a currency at all: it’s an investment vehicle. A tool for making the rich richer. And that’s putting it nicely; in reality it has a lot more in common with a Ponzi scheme than a genuine investment. What “value” does solving fake math problems actually provide to anyone? It’s all bullshit.&lt;/p&gt;&lt;p&gt;And those few failed economies whose people are desperately using cryptocurrency to keep the wheel of their fates spinning? Those make for a good headline, but how about the rural communities whose tax dollars subsidized the power plants which the miners have flocked to? People who are &lt;a href=&quot;https://www.rferl.org/a/bitcoin-blackouts-russian-cryptocurrency-miners-minting-millions-sucking-abkhazia-electricity-grid-dry/30968307.html&quot; target=&quot;_blank&quot;&gt;suffering blackouts&lt;/a&gt; as their power is siphoned into computing SHA-256 as fast as possible while dumping an entire country worth of CO₂ into the atmosphere?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; No, cryptocurrency does not help failed states. It exploits them.&lt;/p&gt;&lt;p&gt;Even those in the (allegedly) working economies of the first world have been impacted by cryptocurrency. The price of consumer GPUs have gone sharply up in the past few months. And, again, what are these GPUs being used for? Running SHA-256 in a loop, as fast as possible. Rumor has it that hard drives are up next.&lt;/p&gt;&lt;p&gt;Maybe your cryptocurrency is different. But look: you’re in really poor company. When you’re the only honest person in the room, maybe you should be in a different room. It is impossible to trust you. Every comment online about cryptocurrency is tainted by the fact that the commenter has probably invested thousands of dollars into a Ponzi scheme and is depending on your agreement to make their money back.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Not to mention that any attempts at reform, like proof-of-stake, are viciously blocked by those in power (i.e. those with the money) because of any risk it poses to reduce their bottom line. No, your blockchain is not different.&lt;/p&gt;&lt;p&gt;Cryptocurrency is one of the worst inventions of the 21st century. I am ashamed to share an industry with this exploitative grift. It has failed to be a useful currency, invented a new class of internet abuse, further enriched the rich, wasted staggering amounts of electricity, hastened climate change, ruined hundreds of otherwise promising projects, provided a climate for hundreds of scams to flourish, created shortages and price hikes for consumer hardware, and injected perverse incentives into technology everywhere. Fuck cryptocurrency.&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;A personal note&lt;/summary&gt;
  &lt;p&gt;
  This rant has been a long time coming and is probably one of the most
  justified expressions of anger I&apos;ve written for this blog yet. However, it
  will probably be the last one.
  &lt;/p&gt;

  &lt;p&gt;
  I realize that my blog has been a source of a lot of negativity in the past,
  and I regret how harsh I&apos;ve been with some of the projects I&apos;ve criticised. I
  will make my arguments by example going forward: if I think we can do better,
  I&apos;ll do it better, instead of criticising those who are just earnestly trying
  their best.
  &lt;/p&gt;

  &lt;p&gt;
  Thanks for reading 🙂 Let&apos;s keep making the software world a better place.
  &lt;/p&gt;
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Cryptocurrency-is-a-disaster/</link>
        
        <pubDate>Mon, 26 Apr 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Cryptocurrency-is-a-disaster/</guid>
      </item>
    
      <item>
        
        
          <title>Parsers all the way down: writing a self-hosting parser</title>
          <description>
            &lt;p&gt;One of the things we’re working on in &lt;a href=&quot;https://drewdevault.com/2021/03/19/A-new-systems-language.html&quot; target=&quot;_blank&quot;&gt;my new programming language&lt;/a&gt; is a self-hosting compiler. Having a self-hosted compiler is a critical step in the development of (some) programming languages: it signals that the language is mature enough to be comfortably used to implement itself. While this isn’t right for some languages (e.g. shell scripts), for a systems programming language like ours, this is a crucial step in our bootstrapping plan. Our self-hosted parser design was completed this week, and today I’ll share some details about how it works and how it came to be.&lt;/p&gt;&lt;p&gt;This is the third parser which has been implemented for this language. We wrote a sacrificial compiler prototype upfront to help inform the language design, and that first compiler used &lt;a href=&quot;https://en.wikipedia.org/wiki/Yacc&quot; target=&quot;_blank&quot;&gt;yacc&lt;/a&gt; for its parser. Using yacc was helpful at first because it makes it reasonably simple to iterate on the parser when the language is still undergoing frequent and far-reaching design changes. Another nice side-effect starting with a yacc parser is that it makes it quite easy to produce a formal grammar when you settle on the design. Here’s a peek at some of our original parser code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;struct_type
	: T_STRUCT &amp;apos;{&amp;apos; struct_fields &amp;apos;}&amp;apos; {
		$$.flags = 0;
		$$.storage = TYPE_STRUCT;
		allocfrom((void **)&amp;amp;$$.fields, &amp;amp;$3, sizeof($3));
	}
	| T_UNION &amp;apos;{&amp;apos; struct_fields &amp;apos;}&amp;apos; {
		$$.flags = 0;
		$$.storage = TYPE_UNION;
		allocfrom((void **)&amp;amp;$$.fields, &amp;amp;$3, sizeof($3));
	}
	;

struct_fields
	: struct_field
	| struct_field &amp;apos;,&amp;apos; { $$ = $1; }
	| struct_field &amp;apos;,&amp;apos; struct_fields {
		$$ = $1;
		allocfrom((void **)&amp;amp;$$.next, &amp;amp;$3, sizeof($3));
	}
	;

struct_field
	: T_IDENT &amp;apos;:&amp;apos; type {
		$$.name = $1;
		allocfrom((void**)&amp;amp;$$.type, &amp;amp;$3, sizeof($3));
		$$.next = NULL;
	}
	;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This approach has you writing code which is already almost a formal grammar in its own right. If we strip out the C code, we get the following:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;struct_type
	: T_STRUCT &amp;apos;{&amp;apos; struct_fields &amp;apos;}&amp;apos;
	| T_UNION &amp;apos;{&amp;apos; struct_fields &amp;apos;}&amp;apos;
	;

struct_fields
	: struct_field
	| struct_field &amp;apos;,&amp;apos;
	| struct_field &amp;apos;,&amp;apos; struct_fields
	;

struct_field
	: T_IDENT &amp;apos;:&amp;apos; type
	;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This gives us a reasonably clean path to writing a formal grammar (and specification) for the language, which is what we did next.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/00bba3bd.png&quot;&gt;
&lt;figcaption&gt;A screenshot of a PDF file which shows a formal grammar similar to the sample given above.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;All of these samples describe a struct type. The following example shows what this grammar looks like in real code — starting from the word “struct” and including up to the “}” at the end.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;coordinates&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;constant field variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;constant field variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to feed our parser tokens to work with, we also need a lexer, or a &lt;em&gt;lexical analyzer&lt;/em&gt;. This turns a series of characters like “struct” into a single token, like the T_STRUCT we used in the yacc code. Like the original compiler used yacc as a parser generator, we also used &lt;a href=&quot;https://en.wikipedia.org/wiki/Lex_(software)&quot; target=&quot;_blank&quot;&gt;lex&lt;/a&gt; as a lexer generator. It’s simply a list of regexes and the names of the tokens that match those regexes, plus a little bit of extra code to do things like turning “1234” into an int with a value of 1234. Our lexer also kept track of line and column numbers as it consumed characters from input files.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;quot;struct&amp;quot;	{ _lineno(); return T_STRUCT; }
&amp;quot;union&amp;quot;		{ _lineno(); return T_UNION; }
&amp;quot;{&amp;quot;		{ _lineno(); return &amp;apos;{&amp;apos;; }
&amp;quot;}&amp;quot;		{ _lineno(); return &amp;apos;}&amp;apos;; }

[a-zA-Z][a-zA-Z0-9_]* {
	_lineno();
	yylval.sval = strdup(yytext);
	return T_IDENTIFIER;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After we settled on the design with our prototype compiler, which was able to compile some simple test programs to give us a feel for our language design, we set it aside and wrote the specification, and, alongside it, a second compiler. This new compiler was written in C — the language was not ready to self-host yet — and uses a hand-written &lt;a href=&quot;https://en.wikipedia.org/wiki/Recursive_descent_parser&quot; target=&quot;_blank&quot;&gt;recursive descent&lt;/a&gt; parser.&lt;/p&gt;&lt;p&gt;To simplify the parser, we deliberately designed a context-free LL(1) grammar, which means it (a) can parse an input unambiguously without needing additional context, and (b) only requires one token of look-ahead. This makes our parser design a lot simpler, which was a deliberate goal of the language design. Our hand-rolled lexer is &lt;em&gt;slightly&lt;/em&gt; more complicated: it requires two characters of lookahead to distinguish between the “.”, “..”, and “…” tokens.&lt;/p&gt;&lt;p&gt;I’ll skip going in depth on the design of the second parser, because the hosted parser is more interesting, and a pretty similar design anyway. Let’s start by taking a look at our hosted lexer. Our lexer is initialized with an input source (e.g. a file) from which it can read a stream of characters. Then, each time we need a token, we’ll ask it to read the next one out. It will read as many characters as it needs to unambiguously identify the next token, then hand it up to the caller.&lt;/p&gt;&lt;p&gt;Our specification provides some information to guide the lexer design:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A token is the smallest unit of meaning in the &lt;span style=&quot;background:
black&quot;&gt;****&lt;/span&gt; grammar. The lexical analysis phase processes a UTF-8 source file to produce a stream of tokens by matching the terminals with the input text.&lt;/p&gt;&lt;p&gt;Tokens may be separated by &lt;em&gt;white-space&lt;/em&gt; characters, which are defined as the Unicode code-points &lt;code&gt;U+0009&lt;/code&gt; (horizontal tabulation), &lt;code&gt;U+000A&lt;/code&gt; (line feed), and &lt;code&gt;U+0020&lt;/code&gt; (space). Any number of whitespace characters may be inserted between tokens, either to disambiguate from subsequent tokens, or for aesthetic purposes. This whitespace is discarded during the lexical analysis phase.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Within a single token, white-space is meaningful. For example, the string-literal token is defined by two quotation marks &lt;strong&gt;”&lt;/strong&gt; enclosing any number of literal characters. The enclosed characters are considered part of the string-literal token and any whitespace therein is not discarded.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The lexical analysis process consumes Unicode characters from the source file input until it is exhausted, performing the following steps in order: it shall consume and discard white-space characters until a non-white-space character is found, then consume the longest sequence of characters which could constitute a token, and emit it to the token stream.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;There are a few different kinds of tokens our lexer is going to need to handle: operators, like “+” and “-”; keywords, like “struct” and “return”; user-defined identifiers, like variable names; and constants, like string and numeric literals.&lt;/p&gt;&lt;p&gt;In short, given the following source code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;add2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to return the following sequence of tokens:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;fn      (keyword)
add2    (identifier)
(       (operator)
x
:
int
,
y
int
)
int
=
x
+
y
;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This way, our parser doesn’t have to deal with whitespace, or distinguishing “int” (keyword) from “integer” (identifier), or handling invalid tokens like “$”. To actually implement this behavior, we’ll start with an initialization function which populates a state structure.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Initializes a new lexer for the given input stream. The path is borrowed.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;loc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;un&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;rb&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;lexer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;rb&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;rune&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This state structure holds, respectively:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The input I/O stream&lt;/li&gt;&lt;li&gt;The path to the current input file&lt;/li&gt;&lt;li&gt;The current (line, column) number&lt;/li&gt;&lt;li&gt;A buffer of un-read characters from the input, for lookahead&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The main entry point for doing the actual lexing will look like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Returns the next token from the lexer.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// A single lexical token, the value it represents, and its location in a file.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// A token value, used for tokens such as &amp;apos;1337&amp;apos; (an integer).&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;rune&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;i64&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;f64&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// A location in a source file.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// A lexical token class.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;ltok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;UNDERSCORE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ABORT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ALLOC&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;APPEND&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;type comment spell&quot;&gt;// ... continued ...&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The idea is that when the caller needs another token, they will call &lt;code&gt;lex&lt;/code&gt;, and receive either a token or an error. The purpose of our lex function is to read out the next character and decide what kind of tokens it might be the start of, and dispatch to more specific lexing functions to handle each case.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;rune&lt;/span&gt; &lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;nextw&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;mkloc&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rl&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;rune&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;loc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;rl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;is_name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;unget&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lex_name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;ascii&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;isdigit&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;unget&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lex_literal&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;ltok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;syntaxerr&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;invalid character&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&amp;quot;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error character string_escape&quot;&gt;\&amp;apos;&lt;/span&gt;&lt;span class=&quot;error character&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;unget&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lex_rn_str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;.&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;&amp;lt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;&amp;gt;&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lex3&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;^&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;*&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;%&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;/&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;+&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;-&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;:&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;!&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;&amp;amp;&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;|&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;character&quot;&gt;&amp;apos;=&amp;apos;&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;lex2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rn&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;~&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;BNOT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;,&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;COMMA&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;{&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;LBRACE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;[&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;LBRACKET&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;(&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;LPAREN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;}&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RBRACE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;]&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RBRACKET&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;)&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;RPAREN&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;;&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;SEMICOLON&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;character&quot;&gt;&amp;apos;?&amp;apos;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;QUESTION&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aside from the EOF case, and simple single-character operators like “;”, both of which this function handles itself, its role is to dispatch work to various sub-lexers.&lt;/p&gt;&lt;details&gt;
	&lt;summary&gt;Expand me to read the helper functions&lt;/summary&gt;
&lt;pre&gt;
fn nextw(lex: *lexer) ((rune, location) | io::EOF | io::error) = {
	for (true) {
		let loc = mkloc(lex);
		match (next(lex)) {
			e: (io::error | io::EOF) =&gt; return e,
			r: rune =&gt; if (!ascii::isspace(r)) {
				return (r, loc);
			} else {
				free(lex.comment);
				lex.comment = &quot;&quot;;
			},
		};
	};
	abort();
};
&lt;/pre&gt;

&lt;pre&gt;
fn unget(lex: *lexer, r: (rune | io::EOF)) void = {
	if (!(lex.rb[0] is void)) {
		assert(lex.rb[1] is void, &quot;ungot too many runes&quot;);
		lex.rb[1] = lex.rb[0];
	};
	lex.rb[0] = r;
};
&lt;/pre&gt;

&lt;pre&gt;
fn is_name(r: rune, num: bool) bool =
	ascii::isalpha(r) || r == &apos;_&apos; || r == &apos;@&apos; || (num &amp;&amp; ascii::isdigit(r));
&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;The sub-lexers handle more specific cases. The lex_name function handles things which look like identifiers, including keywords; the lex_literal function handles things which look like literals (e.g. “1234”); lex_rn_str handles rune and string literals (e.g. “hello world” and ‘\n’); and lex2 and lex3 respectively handle two- and three-character operators like “&amp;&amp;” and “&gt;&gt;=”.&lt;/p&gt;&lt;p&gt;lex_name is the most complicated of these. Because the only thing which distinguishes a keyword from an identifier is that the former matches a specific list of strings, we start by reading a “name” into a buffer, then binary searching against a list of known keywords to see if it matches something there. To facilitate this, “bmap” is a pre-sorted array of keyword names.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bmap&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_special type&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// Keep me alpha-sorted and consistent with the ltok enum.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;_&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;abort&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;alloc&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;append&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;as&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;assert&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;bool&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error comment spell&quot;&gt;// ...&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lex_name&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;keyword&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;rune&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;is_name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;appendrune&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;comment spell&quot;&gt;// Invariant&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;rune&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;is_name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;unget&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;appendrune&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;keyword&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bmap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;LAST_KEYWORD&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;namecmp&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bmap&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/=&lt;/span&gt; &lt;span class=&quot;function_builtin&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rest of the code is more of the same, but I’ve &lt;a href=&quot;https://paste.sr.ht/~sircmpwn/25871787b0d41db2b0af573ba1c93e1b6438b942&quot; target=&quot;_blank&quot;&gt;put it up here&lt;/a&gt; if you want to read it.&lt;/p&gt;&lt;p&gt;Let’s move on to parsing: we need to turn this one dimensional stream of tokens into an structured form: the &lt;em&gt;Abstract Syntax Tree&lt;/em&gt;. Consider the following sample code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;add2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our token stream looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;let x : int = add2 ( 40 , 2 ) ;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But what we need is something more structured, like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;binding
	name=&amp;quot;x&amp;quot;
	type=&amp;quot;int&amp;quot;
	initializer=call-expression
	=&amp;gt;	func=&amp;quot;add2&amp;quot;
		parameters
			constant value=&amp;quot;40&amp;quot;
			constant value=&amp;quot;2&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We know at each step what kinds of tokens are valid in each situation. After we see “let”, we know that we’re parsing a binding, so we look for a name (“x”) and a colon token, a type for the variable, an equals sign, and an expression which initializes it. To parse the initializer, we see an identifier, “add2”, then an open parenthesis, so we know we’re in a call expression, and we can start parsing arguments.&lt;/p&gt;&lt;p&gt;To make our parser code expressive, and to handle errors neatly, we’re going to implement a few helper function that lets us describe these states in terms of what the parser wants from the lexer. We have a few functions to accomplish this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// Requires the next token to have a matching ltok. Returns that token, or an error.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;parameter constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprintf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;apos;{}&amp;apos;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;tokstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;mkloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;fprint&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;syntaxerr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;mkloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Unexpected &amp;apos;{}&amp;apos;, was expecting {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;tokstr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strio&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Looks for a matching ltok from the lexer, and if not present, unlexes the&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// token and returns void. If found, the token is consumed from the lexer and is&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// returned.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;unlex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Looks for a matching ltok from the lexer, unlexes the token, and returns&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// it; or void if it was not a ltok.&lt;/span&gt;
&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;unlex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s say we’re looking for a binding like our sample code to show up next. The grammar from the spec is as follows:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://redacted.moe/f/f0e1d07d.png&quot;&gt;&lt;/p&gt;&lt;p&gt;And here’s the code that parses that:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error parameter constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;is_static&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;STATIC&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error keyword_operator&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;is_const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LET&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;CONST&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LET&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;CONST&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;bindings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;field number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;btype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;_type&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
			&lt;span class=&quot;conditional&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;COLON&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;keyword_operator&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EQUAL&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bindings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;binding&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;_type&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;btype&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;conditional&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;COMMA&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error punctuation_special&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;binding_expr&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;is_static&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;is_static&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;is_const&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;is_const&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;constant field variable&quot;&gt;bindings&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bindings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hopefully the flow of this code is fairly apparent. The goal is to fill in the following AST structure:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// A single variable biding. For example:&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// 	foo: int = bar&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;binding&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_qualifier type&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// A variable binding expression. For example:&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// 	let foo: int = bar, ...&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;binding_expr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;is_static&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;is_const&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;bindings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rest of the code is pretty similar, though some corners of the grammar are a bit hairier than others. One example is how we parse infix operators for binary arithmetic expressions (such as “2 + 2”):&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;binarithm&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;lvalue&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;parameter constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// Precedence climbing parser&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// https://en.wikipedia.org/wiki/Operator-precedence_parser&lt;/span&gt;
	&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lvalue&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lvalue&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constructor function_builtin constant function_call variable&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;precedence&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;precedence&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;binop_for_tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rvalue&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;precedence&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;precedence&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;unlex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;rvalue&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;binarithm&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rvalue&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;expr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;binarithm_expr&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;lvalue&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lvalue&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;constant field variable&quot;&gt;rvalue&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;rvalue&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;lvalue&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

	&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;unlex&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lvalue&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;error keyword_function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;precedence&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;conditional&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;tok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error field number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LOR&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LXOR&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LAND&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LEQUAL&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;NEQUAL&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LESS&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LESSEQ&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;GREATER&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;GREATEREQ&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;BOR&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;BXOR&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;BAND&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;LSHIFT&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;RSHIFT&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;PLUS&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;MINUS&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;TIMES&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;DIV&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;ltok&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;MODULO&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don’t really grok this algorithm, to be honest, but hey, it works. Whenever I write a precedence climbing parser, I’ll stare at the Wikipedia page for 15 minutes, quickly write a parser, and then immediately forget how it works. Maybe I’ll write a blog post about it someday.&lt;/p&gt;&lt;p&gt;Anyway, ultimately, this code lives in our standard library and is used for several things, including our (early in development) self-hosted compiler. Here’s an example of its usage, taken from our documentation generator:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;ast&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;subunit&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error conditional&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant type variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error string&quot;&gt;&amp;quot;Error reading {}: {}&amp;quot;&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;strerror&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error keyword_return&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error type_qualifier&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type method_call variable&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable namespace&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant type variable&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;COMMENTS&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword_return&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;subunit&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where the “ast::subunit” type is:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// A sub-unit, typically representing a single source file.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;subunit&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;imports&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;decls&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;decl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty straightforward! Having this as part of the standard library should make it much easier for users to build language-aware tooling with the language itself. We also plan on having our type checker in the stdlib as well. This is something that I drew inspiration for from Golang — having a lot of their toolchain components in the standard library makes it really easy to write Go-aware tools.&lt;/p&gt;&lt;p&gt;So, there you have it: the next stage in the development of our language. I hope you’re looking forward to it!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Our-self-hosted-parser-design/</link>
        
        <pubDate>Thu, 22 Apr 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Our-self-hosted-parser-design/</guid>
      </item>
    
      <item>
        
          <title>🚀 Signal boost: Why Lichess will always be free</title>
          <description>
            <p>🚀 Signal boost: someone else's article that I think you should read</p>
            <p>
              <a href="https://lichess.org/blog/YF-ZORQAACAA89PI/why-lichess-will-always-be-free">Why Lichess will always be free</a>
            </p>
          </description>
          <link>https://drewdevault.com/blog/Lichess/</link>
        
        
        <pubDate>Thu, 22 Apr 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Lichess/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, April 2021</title>
          <description>
            &lt;p&gt;Another month goes by! I’m afraid that I have very little to share this month. You can check out the &lt;a href=&quot;https://sourcehut.org/blog/2021-04-15-whats-cooking-april-2021/&quot; target=&quot;_blank&quot;&gt;sourcehut “what’s cooking” post&lt;/a&gt; for sourcehut news, but outside of that I have focused almost entirely on the programming language project this month, for which the details are kept private.&lt;/p&gt;&lt;p&gt;The post &lt;a href=&quot;https://drewdevault.com/2021/03/19/A-new-systems-language.html&quot; target=&quot;_blank&quot;&gt;calling for contributors&lt;/a&gt; led to a lot of answers and we’ve brought several new people on board — thanks for answering the call! I’d like to narrow the range of problems we still need help with. If you’re interested in (and experienced in) the following problems, we need your help:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cryptography&lt;/li&gt;&lt;li&gt;Date/time support&lt;/li&gt;&lt;li&gt;Networking (DNS is up next)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Shoot me an email&lt;/a&gt; if you want to help. We don’t have the bandwidth to mentor inexperienced programmers right now, so please only reach out if you have an established background in systems programming.&lt;/p&gt;&lt;p&gt;Here’s a teaser of one of the stdlib APIs written by our new contributors, unix::passwd:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;comment spell&quot;&gt;// A Unix-like group file entry.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;constant type_definition variable&quot;&gt;grent&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword type&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_bracket type&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;type comment spell&quot;&gt;// Name of the group&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;constant field type variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;type&quot;&gt; &lt;/span&gt;&lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// Optional encrypted password&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// Numerical group ID&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;comment spell&quot;&gt;// List of usernames that are members of this group, comma separated&lt;/span&gt;
	&lt;span class=&quot;constant field variable&quot;&gt;userlist&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Reads a Unix-like group entry from a stream. The caller must free the result&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// using [grent_finish].&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;nextgr&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;punctuation_special type operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;grent&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;EOF&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter type&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Frees resources associated with [grent].&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;grent_finish&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;ent&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant type variable&quot;&gt;grent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment spell&quot;&gt;// Looks up a group by name in a Unix-like group file. It expects a such file at&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// /etc/group. Aborts if that file doesn&amp;apos;t exist or is not properly formatted.&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;comment spell&quot;&gt;// See [nextgr] for low-level parsing API.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;getgroup&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;parameter constant variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket type&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant type variable&quot;&gt;grent&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s all for now. These updates might be light on details for a while as we work on this project. See you next time!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-April-2021/</link>
        
        <pubDate>Thu, 15 Apr 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-April-2021/</guid>
      </item>
    
      <item>
        
        
          <title>The Developer Certificate of Origin is a great alternative to a CLA</title>
          <description>
            &lt;p&gt;Today Amazon released their fork of ElasticSearch, &lt;a href=&quot;https://github.com/opensearch-project/OpenSearch&quot; target=&quot;_blank&quot;&gt;OpenSearch&lt;/a&gt;, and I want to take a moment to draw your attention to one good decision in particular: its use of the &lt;a href=&quot;https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin&quot; target=&quot;_blank&quot;&gt;Developer Certificate of Origin&lt;/a&gt; (or “DCO”).&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Previously:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/blog/Elasticsearch-does-not-belong-to-Elastic/&quot;&gt;ElasticSearch does not belong to Elastic&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/blog/FOSS-is-to-surrender-your-monopoly/&quot;&gt;Open source means surrendering your monopoly over commercial exploitation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://drewdevault.com/blog/Dont-sign-a-CLA/&quot;&gt;Don’t sign a CLA&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;p&gt;Elastic betrayed its community when they changed to a proprietary license.  We could have seen it coming because of a particular trait of their contribution process: the use of a Contributor License Agreement, or CLA. In principle, a CLA aims to address legitimate concerns of ownership and copyright, but in practice, they are a promise that one day the stewards of the codebase will take your work and relicense it under a nonfree license. And, ultimately, this is exactly what Elastic did, and exactly what most other projects which ask you to sign a CLA are &lt;em&gt;planning&lt;/em&gt; to do. If you ask me, that’s a crappy deal, and I refrain from contributing to those projects as a result.&lt;/p&gt;&lt;p&gt;However, there are some legitimate questions of ownership which a project owner might rightfully wish to address before accepting a contribution. As is often the case, we can look to git itself for an answer to this problem. Git was designed for the Linux kernel, and patch ownership is a problem they faced and solved a long time ago. Their answer is the &lt;a href=&quot;https://developercertificate.org/&quot; target=&quot;_blank&quot;&gt;Developer Certificate of Origin&lt;/a&gt;, or DCO, and tools for working with it are already built into git.&lt;/p&gt;&lt;p&gt;git provides the -s flag for git commit, which adds the following text to your commit message:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Signed-off-by: Drew DeVault &amp;lt;drew@ddevault.org&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The specific meaning varies from project to project, but it is usually used to indicate that you have read and agreed to the DCO, which reads as follows:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;By making a contribution to this project, I certify that:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or&lt;/li&gt;&lt;li&gt;The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or&lt;/li&gt;&lt;li&gt;The contribution was provided directly to me by some other person who certified (1), (2) or (3) and I have not modified it.&lt;/li&gt;&lt;li&gt;I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;&lt;p&gt;This neatly answers all concerns of copyright. You license your contribution under the original license (Apache 2.0 in the case of OpenSearch), and attest that you have sufficient ownership over your changes to do so. You retain your copyright and you don’t leave the door open for the maintainers to relicense your work under some other terms in the future. This offers the maintainers the same rights that they extended to the community themselves.&lt;/p&gt;&lt;p&gt;This is the strategy that Amazon chose for OpenSearch, and it’s a good thing they did, because it strongly signals to the community that it will not fall to the same fate that ElasticSearch has. By doing this, they have imposed on themselves a great deal of difficulty to any future attempt to change their copyright obligations. I applaud Amazon for this move, and I’m optimistic about the future of OpenSearch under their stewardship.&lt;/p&gt;&lt;p&gt;If you have a project of your own that is concerned about the copyright of third-party contributions, then please consider adopting the DCO instead of a CLA. And, as a contributor, if someone asks you to sign a CLA, consider withholding your contribution: a CLA is a promise to the contributors that someday their work will be taken from them and monetized to the exclusive benefit of the project’s lords. This affects my personal contributions, too — for example, I avoid contributing to Golang as a result of their CLA requirement. Your work is important, and the projects you offer it to should respect that.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/DCO/</link>
        
        <pubDate>Mon, 12 Apr 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/DCO/</guid>
      </item>
    
      <item>
        
        
          <title>What should the next chat app look like?</title>
          <description>
            &lt;p&gt;As you’re surely aware, Signal has officially jumped the shark with the introduction of cryptocurrency to their chat app. Back in 2018, I &lt;a href=&quot;https://drewdevault.com/2018/08/08/Signal.html&quot; target=&quot;_blank&quot;&gt;wrote about my concerns with Signal&lt;/a&gt;, and those concerns were unfortunately validated by this week’s announcement. Moxie’s insistence on centralized ownership, governance, and servers for Signal puts him in a position of power which is easily, and inevitably, abused. In that 2018 article, and in &lt;a href=&quot;https://drewdevault.com/2020/09/20/The-potential-of-federation.html&quot; target=&quot;_blank&quot;&gt;articles since&lt;/a&gt;, I have spoken about the important of federation to address these problems. In addition to federation, what else does a chat app need?&lt;/p&gt;&lt;p&gt;Well, first, the next chat app should be &lt;strong&gt;a protocol&lt;/strong&gt;, not just an app. A lush ecosystem of client and server implementations, along with bots and other integrations, adds a tremendous amount of value and longevity to a system. A chat app which has only one implementation and a private protocol can only ever meet the needs that its developers (1) foresee, (2) care about, and (3) have the capacity to address; thus, such a protocol cannot be ubiquitous. I would also recommend that this protocol is not needlessly stapled to the beached whale that is the web: &lt;em&gt;maybe&lt;/em&gt; JSON can come, but if it’s served with HTTP polling to appease our Android overlords I will be very cross with you. JSON also offers convenient extensibility, and a protocol designer who limits extensibility is a wise one.&lt;/p&gt;&lt;p&gt;Crucially, that protocol &lt;em&gt;must&lt;/em&gt; be &lt;strong&gt;federated&lt;/strong&gt;. This is Signal’s largest failure. We simply cannot trust a single entity, even you, dear reader, to have such a large degree of influence over the ecosystem.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; I do not trust you not to add some crypto Ponzi scheme of your own 5 years from now. A federated system allows multiple independent server operators to stand up their own servers which can communicate with each other and exchange messages on behalf of their respective users, which distributes ownership, responsibility, and governance within the community at large, making the system less vulnerable to all kinds of issues. You need to be prepared to relinquish control to the community. Signal wasn’t, and has had problems ranging from 502 Server Gone errors to 404 Ethics Not Found errors, both of which are solved by federation.&lt;/p&gt;&lt;p&gt;The next chat app also needs &lt;strong&gt;end-to-end encryption&lt;/strong&gt;. This should be fairly obvious, but it’s worth re-iterating because this will occupy a &lt;em&gt;majority&lt;/em&gt; of the design work that goes into the app. There are complex semantics involved in encrypting user-to-user chats, group chats (which could add or remove users at any time), perfect forward secrecy, or multiple devices under one account; many of these issues have implications for the user experience. This is complicated further by the concerns of a federated design, and if you want to support voice or video chat (please don’t), that’ll complicate things even more. You’ll spend the bulk of your time solving these problems. I would advise, however, that you let users dial down the privacy (after explaining to them the trade-offs) in exchange for convenience. For instance, to replace IRC you would need to support channels which anyone can join at any time and which might make chat logs available to the public.&lt;/p&gt;&lt;p&gt;A new chat app also needs &lt;strong&gt;anonymity&lt;/strong&gt;. None of this nonsense where users have to install your app and give you their phone number to register. In fact, you should know next to nothing about each user, given that the most secure data is the data you don’t have. This is made more difficult when you consider that you’ll also strive to provide an authentic identity for users to establish between themselves — but not with you. Users should also be able to establish a pseudonymous identity, or wear multiple identities. You need to provide (1) a strong guarantee of consistent identity from session to session, (2) without sharing that guarantee with your servers, and (3) offer the ability to able to change to a new identity at will. The full implications of anonymity are a complex issue which is out of scope for this article, but for now it suffices to say that you should at least refrain from asking for the user’s phone number.&lt;/p&gt;&lt;p&gt;Finally, it needs to be &lt;strong&gt;robust, reliable, and performant&lt;/strong&gt;. Focus on the basics: delivering messages quickly and reliably. The first thing you need to produce is a reliable messenger which works in a variety of situations, on a variety of platforms, in various network conditions, and so on, with the underlying concerns of federation, end-to-end encryption, protocol standardization, group and individual chats, multi-device support, and so on, in place and working. You can try to deliver this in a moderately attractive interface, but sinking a lot of time into fancy animations, stickers, GIF lookups, typing notifications and read receipts — all of this is a distraction until you get the real work done. You can have all of these things, but if you don’t have a reliable system underlying them, the result is worthless.&lt;/p&gt;&lt;p&gt;I would also recommend leaving a lot of those features at the door, anyway. Typing notifications and read receipts are pretty toxic, if you examine them critically. A lot of chat apps have a problem with cargo-culting bad ideas from each other. Try to resist that. Anyway, you have a lot of work to do, so I’ll leave you to it. &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Let me know&lt;/a&gt; what you’re working on when you’ve got something to show for it.&lt;/p&gt;&lt;p&gt;And don’t put a fucking cryptocurrency in it.&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;Regarding Matrix, IRC, etc&lt;/summary&gt;
  &lt;p&gt;
    Let&apos;s quickly address the present state of the ecosystem. Matrix rates well
    in most of these respects, much better than others. However, their software
    is way too complicated. They are federated, but the software is far from
    reliable or robust, so the ecosystem tends to be centralized because
    Matrix.org are the only ones who have the knowledge and bandwidth to keep it
    up and running. The performance sucks, client and server both, and their UX
    for E2EE is confusing and difficult to use.
  &lt;/p&gt;

  &lt;p&gt;
    It&apos;s a good attempt, but too complex and brittle. Also, their bridge
    is a major nuisance to IRC, which biases me against them. Please don&apos;t
    integrate your next chat app with IRC; just leave us alone, thanks.
  &lt;/p&gt;

  &lt;p&gt;
    Speaking of IRC, it is still my main chat program, and has been for 15+
    years. The lack of E2EE, which is unacceptable for any new protocol, is not
    important enough to get me to switch to anything else until it presents a
    compelling alternative to IRC.
  &lt;/p&gt;
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-next-chat-app/</link>
        
        <pubDate>Wed, 07 Apr 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-next-chat-app/</guid>
      </item>
    
      <item>
        
        
          <title>Go is a great programming language</title>
          <description>
            &lt;p&gt;No software is perfect, and thus even for software I find very pleasant, I can usually identify some problems in it — often using my blog to do so. Even my all-time favorite software project, Plan 9, has some painful flaws! For some projects, it may be my fondness for them that drives me to criticise them even more, in the hope that they’ll live up to the level of respect I feel for them.&lt;/p&gt;&lt;p&gt;One such project is the Go programming language. I have had many criticisms, often shared on this blog and elsewhere, but for the most part, my praises have been aired mainly in private. I’d like to share some of those praises today, because despite my criticisms of it, Go remains one of the best programming languages I’ve ever used, and I have a great deal of respect for it.&lt;/p&gt;&lt;p&gt;Perhaps the matter I most appreciate Go for is its long-term commitment to simplicity, stability, and robustness. I prize these traits more strongly than any other object of software design. The Go team works with an ethos of careful restraint, with each feature given deliberate consideration towards identifying the simplest and most complete solution, and they carefully constrain the scope of their implementations to closely fit those solutions. The areas where Go has failed in this regard are frightfully scarce.&lt;/p&gt;&lt;p&gt;The benefits of their discipline are numerous. The most impressive accomplishment that I attribute to this approach is the quality of the Go ecosystem at large. In the first place, it is a great accomplishment to produce a language and standard library with the excellence in design and implementation that Go offers, but it’s a truly profound achievement to have produced a design which the community &lt;em&gt;at large&lt;/em&gt; utilizes to make similarly excellent designs as a basic consequence of the language’s simple elegance.  Very few other languages enjoy a similar level of consistency and quality in the ecosystem.&lt;/p&gt;&lt;p&gt;Go is also notable for essentially inventing its own niche, and then helping that niche grow around it into an entirely new class of software design. I consider Go not to be a systems programming language — a title much better earned by languages like C and Rust. Rather, Go is the best-in-class for a new breed of software: an Internet programming language.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; The wealth of network protocols implemented efficiently, concisely, and correctly in its standard library, combined with its clever mixed cooperative/pre-emptive multitasking model, make it very easy to write scalable internet-facing software. A few other languages — Elixir comes to mind — also occupy this niche, but they haven’t enjoyed the runaway success that Go has.&lt;/p&gt;&lt;p&gt;The Go team has also earned my respect for their professionalism. The close degree to which Go is tied to Google comes with its own set of trade-offs, but the centralization of project leadership caused by this relationship is beneficial for the project. Some members of the Go community have noticed the apparent disadvantages of this structure, as Go is infamous for being slow to respond to the wants of its community. This insulation, I would argue, is in fact advantageous for the conservative language design that Go embraces, and may actually be essential to its value-add as a project. If Go listened to the community as much as they want, it would become a kitchen sink, and cease to be interesting to me.&lt;/p&gt;&lt;p&gt;Rather than being closely tied to its community’s wants, Go generally does a much better job of being closely tied to its community’s &lt;em&gt;needs&lt;/em&gt;. If you have correctly identified a problem in Go, when you bring it to their attention, you will be taken seriously. Many projects struggle to separate their egos from the software, and when mistakes are found, they take it personally. Go does an excellent job of treating it like an engineer — a matter-of-fact analysis of the problem, deliberation on the solution, and shipping of a fix.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; Go has a reputation for plain old good engineering.&lt;/p&gt;&lt;p&gt;In short, I admire Go very much, despite my frequent criticisms. I recognize Go as one of the best programming languages ever made. Go has attained an elusive status in the programming canon as a robust engineering tool that can be expected to work, and work well, in its applications for decades to come. Its because of this respect that I hold Go to such a high standard, and I hope that it continues to impress me going forward.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Go-is-a-great-language/</link>
        
        <pubDate>Fri, 02 Apr 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Go-is-a-great-language/</guid>
      </item>
    
      <item>
        
        
          <title>The world&apos;s stupidest IRC bot</title>
          <description>
            &lt;p&gt;I’m an &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Relay_Chat&quot; target=&quot;_blank&quot;&gt;IRC&lt;/a&gt; power user, having been hanging out in 200+ channels on 10+ networks 24/7 for the past 10 years or so. Because IRC is &lt;a href=&quot;https://tools.ietf.org/html/rfc2812&quot; target=&quot;_blank&quot;&gt;standardized&lt;/a&gt; and simple, a common pastime for IRC enthusiasts is the creation of bots. In one of the social channels I hang out in, we’ve spent the past 6 years gradually building the world’s stupidest IRC bot: wormy.&lt;/p&gt;&lt;p&gt;For a start, wormy is highly schizophrenic. Though it presents itself as a single bot, it is in fact a &lt;a href=&quot;https://en.wikipedia.org/wiki/BNC_(software)&quot; target=&quot;_blank&quot;&gt;bouncer&lt;/a&gt; which combines the connections of 7 independent bots. At one point, this number was higher — as many as 11 — but some bots were consolidated.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;@sircmpwn&amp;gt; .bots
&amp;lt;wormy&amp;gt; Serving text/html since 2017, yours truly [&amp;quot;ps&amp;quot;] For a list of commands, try `.help`
&amp;lt;wormy&amp;gt; minus&amp;apos; parcel tracking bot r10.b563abc (built on 2020-06-06T12:02:13Z, https://git.sr.ht/~minus/parcel-tracking-bot)
&amp;lt;wormy&amp;gt; minus&amp;apos; dice bot r16.498a0b8 (built on 2020-02-04T20:16:14Z, https://git.sr.ht/~minus/dice-irc-bot)
&amp;lt;wormy&amp;gt; Featuring arbitrary code execution by design and buffer overflows by mistake, jsbot checking in
&amp;lt;wormy&amp;gt; Radiobot coming to you live from The Internet, taking listener requests at 1-800-GUD-SONGS
&amp;lt;wormy&amp;gt; urlbot: live streaming moe directly to your eyeballs
&amp;lt;wormy&amp;gt; o/ SirCmpwn made me so he wouldn&amp;apos;t forget shit so much
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These bots provide a variety of features for channel members, such as checking tracking numbers for parcels out for delivery, requesting songs for our private internet radio, reading out the mimetypes and titles of URLs mentioned in the channel, or feeding queries into Wolfram Alpha.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;wormy&amp;gt; Now playing: 8369492 小さき者への贖罪の為のソナタ  by ALI PROJECT from 禁書 (4m42s FLAC)
&amp;lt;wormy&amp;gt; Now playing: 1045361 アキノサクラ by Wakana from magic moment (5m0s FLAC) #live ♥ minus
&amp;lt;wormy&amp;gt; Now playing: d0b1cb3 Forevermore by F from Cafe de Touhou 3 (4m9s FLAC) ♥ hummer12007
&amp;lt;wormy&amp;gt; Now playing: 0911e90 Moeru San Shimai by Iwasaki Taku from Tengen Toppa Gurren Lagann Original Soundtrack - CD01 (3m3s FLAC)
&amp;lt;wormy&amp;gt; Now playing: ac1a17e rebellion anthem by Yousei teikoku from rebellion anthem (5m15s MP3) ♥ minus
&amp;lt;wormy&amp;gt; Now playing: a5ab39a Desirable Dream by GET IN THE RING from Aki-秋- (4m38s FLAC) ♥ minus
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Things really took off with the introduction of a truly stupid bot last year: &lt;a href=&quot;https://git.sr.ht/~sircmpwn/jsbot&quot; target=&quot;_blank&quot;&gt;jsbot&lt;/a&gt;. This bot adds a &lt;code&gt;.js&lt;/code&gt; command which executes arbitrary JavaScript (using Fabrice Bellard’s &lt;a href=&quot;https://bellard.org/quickjs/&quot; target=&quot;_blank&quot;&gt;quickjs&lt;/a&gt;) expressions, and sending their stringified result to the channel.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;@sircmpwn&amp;gt; .js Array(16).join(&amp;quot;wat&amp;quot; - 1) + &amp;quot; Batman!&amp;quot;
&amp;lt;wormy&amp;gt; =&amp;gt; NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We soon realized, however, that what we had effectively created was a persistent JavaScript environment which was connected to IRC. This has made it possible to write even more IRC bots in the least practical manner imaginable: by writing JavaScript statements, one line at a time, into IRC messages, and hoping it works.&lt;/p&gt;&lt;p&gt;This has not been an entirely smart move.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/52eb4eba.png&quot;&gt;
&lt;figcaption&gt;I say “I hate C++ templates”, and the bot responds by writing “yeah, fuck C++ templates!” with “C++ templates” displayed in rainbow colors. MartijnBraam follows up by asking “number of c++ programmers launched into the sun”, which wormy claims is 367880 people as of 2009.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;One “feature”, inspired by &lt;a href=&quot;https://www.youtube.com/watch?v=30jNsCVLpAE&quot; target=&quot;_blank&quot;&gt;Bryan Cantrill&lt;/a&gt;, records every time the word “fuck” is used in the channel. Then, whenever anyone says “wtf”, the bot helpfully offers up an example of the usage of the word “fuck” by printing one of the recorded messages. Here’s how it was made:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;sircmpwn&amp;gt; .js let wtf = [];
&amp;lt;wormy&amp;gt;  =&amp;gt; undefined
&amp;lt;sircmpwn&amp;gt; .js on(/fuck/, msg =&amp;gt; wtf.push(msg.text))
&amp;lt;wormy&amp;gt;  =&amp;gt; 25
&amp;lt;sircmpwn&amp;gt; .js on(/^what the fuck$/, msg =&amp;gt; msg.reply(wtf[Math.floor(Math.random() * wtf.length)]))
&amp;lt;wormy&amp;gt;  =&amp;gt; 26
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here’s one which records whenever someone says “foo++” or “foo--” and keeps track of scores:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;.js on(/^([a-zA-Z0-9_]+)(\+\+|--)$/, (msg, thing, op) =&amp;gt; { if (typeof scores[thing] === &amp;quot;undefined&amp;quot;) scores[thing] = 0; scores[thing] += op === &amp;quot;++&amp;quot; ? 1 : -1; msg.reply(`${thing}: ${scores[thing]}`) });
.js on(/\.score (.*)/, (msg, item) =&amp;gt; msg.reply(scores[item]));
.js let worst = () =&amp;gt; Object.entries(scores).sort((a, b) =&amp;gt; a[1] - b[1]).slice(0, 5).map(s =&amp;gt; `${s[0]}: ${s[1]}`).join(&amp;quot;, &amp;quot;);
.js let best = () =&amp;gt; Object.entries(scores).sort((a, b) =&amp;gt; b[1] - a[1]).slice(0, 5).map(s =&amp;gt; `${s[0]}: ${s[1]}`).join(&amp;quot;, &amp;quot;);
.js on(/^.worst$/, msg =&amp;gt; msg.reply(worst()));
.js on(/^.best$/, msg =&amp;gt; msg.reply(best()));
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Other “features” written in horrible one-liners include SI unit conversions, rewriting undesirable URLs (e.g. m.wikipedia.org =&gt; en.wikipedia.org), answering “wormy you piece of shit” with “¯\_(ツ)_/¯”, and giving the obvious response to “make me a sandwich”.&lt;/p&gt;&lt;p&gt;Eventually it occurred to us that we had two dozen stupid IRC bots storing not only their state, but their code, in a single long-lived process on some server. For a while, the answer to this was adding “don’t reboot this server kthx” to the MotD, but eventually we did some magic nonsense to make certain variables persistent:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persistent&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;writePersistent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;persist.json&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persistent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persist_handler&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;property function_method&quot;&gt;set&lt;/span&gt;: &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;writePersistent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;loadFile&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;persist.json&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persistent&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persistent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;proxy&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;Proxy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persistent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persist_handler&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persistent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;persist&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;proxy&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;Proxy&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persist_handler&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;persistent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;writePersistent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anyway, there’s no moral to this story. We just have a silly IRC bot and I thought I’d share that with you. If you want a stupid IRC bot for your own channel, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/jsbot&quot; target=&quot;_blank&quot;&gt;jsbot&lt;/a&gt; is available on sourcehut. I highly disrecommend it and disavow any responsibility for the consequences.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-worlds-dumbest-IRC-bot/</link>
        
        <pubDate>Mon, 29 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-worlds-dumbest-IRC-bot/</guid>
      </item>
    
      <item>
        
        
          <title>The complete guide for open sourcing video games</title>
          <description>
            &lt;p&gt;Video games are an interesting class of software. Unlike most software, they are a creative endeavour, rather than a practical utility. Where most software calls for new features to address practical needs of their users, video games call for new features to serve the creative vision of their makers. Similarly, matters like refactoring and paying down tech debt are often heavily de-prioritized in favor of shipping something ASAP. Many of the collaborative benefits of open source are less applicable to video games. It is perhaps for these reasons that there are very few commercial open source games.&lt;/p&gt;&lt;p&gt;However, there are some examples of such games, and they have had a great deal of influence on gaming.  Id is famous for this, having released the source code for several versions of DOOM. The Quake engine was also released under the GPL, and went on to be highly influential, serving as the basis for dozens of games, including time-honored favorites such as the Half Life series. Large swaths of the gaming canon were made possible thanks to the generous contributions of open source game publishers.&lt;/p&gt;&lt;p&gt;Publishing open source games is also a matter of historical preservation. Proprietary games tend to atrophy. Long after their heyday, with suitable platforms scarce and physical copies difficult to obtain, many games die a slow and quiet death, forgotten to the annals of time. Some games have overcome this by releasing their source code, making it easier for fans to port the game to new platforms and keep it alive.&lt;/p&gt;&lt;p&gt;What will your game’s legacy be? Will it be forgotten entirely, unable to run on contemporary platforms? Will it be source-available, occasionally useful to the devoted player, but with little reach beyond? Perhaps it goes the way of DOOM, living forever in ports to hundreds of devices and operating systems. Maybe it goes the way of Quake, its soul forever a part of the beloved classics of the future. If you keep the source code closed, the only conclusion is the first: enjoyed once, now forgotten.&lt;/p&gt;&lt;p&gt;With this in mind, how do you go about securing your game’s legacy?&lt;/p&gt;&lt;h2&gt;Source available: the bare minimum&lt;/h2&gt;&lt;p&gt;The bare minimum is to make your game “source available”. Be aware that this is not the same thing as making it open source! Some of your famous peers in this category include Alien 3, Civilization IV and V, Crysis, Deus Ex, Prince of Persia, Unreal Tournament, and VVVVVV.&lt;/p&gt;&lt;p&gt;This approach makes your source code available to view and perhaps to compile and run, but prohibits derivative works. This is definitely better than leaving it closed source: it provides helpful resources for modders, speedrunners, and other fans; and devoted players may be able to use it as the basis for getting the game running on future platforms, albeit alone and unable to share their work.&lt;/p&gt;&lt;p&gt;If you choose a minimal enforcement approach, then some players might ultimately share their work, but you’re leaving them on tenuous legal grounds. I would recommend this if you’re very protective of your IP, but know that you’re limiting the potential second life of your game if you take this approach.&lt;/p&gt;&lt;h2&gt;Copyleft with proprietary assets&lt;/h2&gt;&lt;p&gt;The next step up is to make your game open source using a &lt;em&gt;copyleft&lt;/em&gt; license, but refraining from extending the license to the assets — anyone who wants to get the source code working would either need to buy the game from you and extract the assets, or supply their own community-made assets. This is a popular approach among open source games, and gives you most of the benefits and few of the drawbacks. You’ll join the ranks of our DOOM and Quake examples, as well as Amnesia: the Dark Descent, System Shock, Duke Nukem 3D, and Wolfenstein 3D.&lt;/p&gt;&lt;p&gt;Games like this enjoy a long life as their software is more easily ported to new platforms and shared with other users. DOOM runs on phones, digital cameras, ATMs, even toasters! Its legacy is secure without any ongoing commitment from the original developers. This also allows derivatives works — new games based on your code — though it may turn some developers away. Using a copyleft license like the &lt;a href=&quot;https://www.gnu.org/licenses/gpl-3.0.en.html&quot; target=&quot;_blank&quot;&gt;GPL&lt;/a&gt; requires derivative works to &lt;em&gt;also&lt;/em&gt; be made open source. The community generally has no problem with this, but it may affect the willingness of future developers to incorporate your work into their own commercial games. I personally think that the proliferation of open source software that’s implied in the use of a copyleft license is a positive thing — but you may want to use another approach.&lt;/p&gt;&lt;h2&gt;Permissive license, proprietary assets&lt;/h2&gt;&lt;p&gt;If you want to allow your source code to find its way into as many future games as possible, a permissive open source license like &lt;a href=&quot;https://opensource.org/licenses/MIT&quot; target=&quot;_blank&quot;&gt;MIT&lt;/a&gt; is the way to go. &lt;a href=&quot;https://github.com/blendogames/flotilla&quot; target=&quot;_blank&quot;&gt;Flotilla&lt;/a&gt; is an example of a game which went with this approach. It allows developers to incorporate your source code into their own games with little restriction, either by creating a direct derivative, or by taking little samples of your code and incorporating it into their own project. This comes with no obligation to release their own changes or works in a similar fashion: they can just take it, with very few strings attached. Such an approach makes it very easy to incorporate into new commercial games.&lt;/p&gt;&lt;p&gt;This is the most selfless way to release your code. I would recommend this if you don’t care about what happens to your code later, and you just want to make it open source and move on. Though this will definitely enable the largest number of future projects to make use of your work, the copyleft approach is better for ensuring that the largest possible number of future games are &lt;em&gt;also&lt;/em&gt; open source.&lt;/p&gt;&lt;h2&gt;Open assets&lt;/h2&gt;&lt;p&gt;If you’re feeling especially generous, you could release the assets, too. Good licenses for this includes the &lt;a href=&quot;https://creativecommons.org/&quot; target=&quot;_blank&quot;&gt;Creative Commons&lt;/a&gt; licenses. All of them permit free redistribution of your assets, so future players won’t have to buy your game to get them. This could be important if the distribution platform you used is defunct, or if you’re not around to buy it from — consider this well before deciding that you’d rather keep your share of the dwindling asset sales as your game ages.&lt;/p&gt;&lt;p&gt;Using Creative Commons also allows you to tune the degree to which your assets may be re-used. You can choose different CC licenses to control the commercialization of your assets and use in derivative works. To allow free redistribution and nothing else, the CC-NC-ND license (noncommercial, no derivatives) will do the trick. The CC-BY-SA license is the copyleft of creative commons: it will allow free redistribution, commercialization, and derivative works, &lt;em&gt;if&lt;/em&gt; the derivatives are also shared with the same rights. The permissive approach is CC-0, which is equivalent to releasing your assets into the public domain.&lt;/p&gt;&lt;p&gt;Permitting derivatives and re-commercialization of your assets can save a lot of time for new game developers, especially indie devs with a small budget. It’s also cool for making derivative &lt;em&gt;games&lt;/em&gt;, similar to modding, where creative players can remix your assets to make a new game or expansion pack.&lt;/p&gt;&lt;h2&gt;What if I don’t completely own my game?&lt;/h2&gt;&lt;p&gt;You can’t give away the rights to anything you don’t own. If you rely on proprietary libraries, or a third-party level editor, or you don’t own the rights to the music or sprites, you cannot make them open source.&lt;/p&gt;&lt;p&gt;In this situation, I recommend open sourcing everything that you’re able to. This might mean that you open source an ultimately broken game — it simply might not work, or not even compile, without these resources. This is unfortunate, but by releasing everything you can, you leave your community in a good position to fill in the gaps themselves, perhaps by refactoring your code to work around them, or by replacing the proprietary bits with free alternatives. This also allows the parts of your game which are open to be reused in future games.&lt;/p&gt;&lt;h2&gt;But cheaters could use it!&lt;/h2&gt;&lt;p&gt;This is true. And it’s worth noting that if your game has a mandatory online component based on your own servers, then making it open source doesn’t make nearly as much sense, especially if you ultimately decide to shut those servers off.&lt;/p&gt;&lt;p&gt;There is a trade-off to be made here. In truth, it’s very difficult to prevent cheating in your game. If you’ve made a popular competitive multiplayer game, you and I both know that there are still cheaters using it despite your best efforts. Keeping it proprietary is not going to stave off cheaters. Social solutions are better — like a system to report cheaters, or to let friends play on private servers.&lt;/p&gt;&lt;p&gt;Making your game open source might help less skilled script kiddie figure out how to cheat more easily in your game. I can’t decide for you if the trade-off is worth it for your game, but I can tell you that the benefits of making it open are vast, and the efficacy of keeping it closed to prevent cheating is questionable.&lt;/p&gt;&lt;h2&gt;But my code is embarrassing!&lt;/h2&gt;&lt;p&gt;So is everyone else’s. 🙂 We all know that games are running up against tight deadlines and clean code is not going to be the #1 priority. I assure you that your community will be too busy having fun to judge you for the quality of your code. The idea that it just needs to be “cleaned up” first is the death of many projects which would otherwise have been made open source. If you feel this way, you will probably never be satisfied, and thus you’ll never open it. I assure you: your game is ready to make open source, no matter what state it’s in!&lt;/p&gt;&lt;p&gt;Bonus: Ethan Lee tipped me off to some truly awful code which was left in VVVVVV, which you can freely browse on the &lt;a href=&quot;https://github.com/TerryCavanagh/vvvvvv/tree/2.2&quot; target=&quot;_blank&quot;&gt;2.2 tag&lt;/a&gt;. It’s not great, but you probably didn’t know that — you only remember VVVVVV as a critically acclaimed game. Game developers are working under tight constraints and no one is judging them for that — we just want to have fun!&lt;/p&gt;&lt;h2&gt;So what do I need to do?&lt;/h2&gt;&lt;p&gt;Let’s lay out the specific steps. You need to answer the following questions first:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Do I actually own the entire game? What parts am I allowed to open source?&lt;/li&gt;&lt;li&gt;Will I make the code source-available, copyleft, or permissively licensed?&lt;/li&gt;&lt;li&gt;And the assets? Proprietary? Creative Commons? If the latter, which version?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you’re not sure what’s best, I would recommend using the GPL for your code, and CC-BY-SA for the assets. This allows for derivative works, so long as they’re also made open with a similar license. This enables the community to build on your work, porting it to new platforms, building a thriving modding community, and freely sharing your assets, ensuring an enduring legacy for your game. If you’d like to decide the details for yourself, review the comments above once again and pick out the licenses you’d like to use for each before moving on.&lt;/p&gt;&lt;p&gt;If you need help with any of these steps, or have any questions, please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;send me an email&lt;/a&gt;, and I will help you to the best of my ability.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Publishing the source code&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Prepare an archive of your source code, and add the license file. If you went with the source-available approach, simply write “Copyright © &lt;&lt;em&gt;you&lt;/em&gt;&gt; &lt;&lt;em&gt;current year&lt;/em&gt;&gt;. All rights reserved.” into a text file named LICENSE. If you chose something else, copy the license text into a LICENSE file.&lt;/p&gt;&lt;p&gt;If you want this over with quickly, just stick the code and license into a zip file or a tarball and drop it on your website. A better approach, if you have the patience, would be to publish it as a git repository. If you already use version control, you may want to consider carefully if you want to publish your full version control history — the answer might be “yes”, but if you’re unsure, the answer is probably “no”. Just make a copy of the code, delete the .git directory, and import it into a new repository if you need to.&lt;/p&gt;&lt;p&gt;Double check that you aren’t checking in any artifacts — assets, executables, libraries, etc — and then push it to the hosting service of your choice. GitHub is a popular choice, but I would selfishly recommend &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;sourcehut&lt;/a&gt; as well. If you have time, write a little README file which gives an introduction to the project as well.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Publishing the assets&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If you choose to leave the assets proprietary, then there are no further steps. Players can figure out how to extract the assets from their purchased game.&lt;/p&gt;&lt;p&gt;If you choose to make them open, prepare an archive of your assets. Include a copy of the license you choose — e.g. which Creative Commons license you used — and drop it into a zip file or a tarball or something similar. Stick this on your website, and if you’re feeling generous, prepare some instructions for how to incorporate the asset bundle into the game once a player compiles your code.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Tell the world!&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Let everyone know that you’ve made your game open source! Write a little blog post, link to the source and assets, and enjoy a little bit more of the limelight while the press and the community thanks you for your contribution.&lt;/p&gt;&lt;p&gt;One final request on this note: if you choose the source-available approach, please refer to it as such in your public statements. Source available is &lt;em&gt;not&lt;/em&gt; the same thing as “open source”, and the distinction is important.&lt;/p&gt;&lt;p&gt;And now it’s my turn to thank you: I’m so happy that you’ve released your game as an open source project! The community is much richer for your contribution to it, and I hope that your game will live on for many years to come, both in self through ports and mods, and in spirit through its contributions to future games. You’ve done a wonderful thing. Thank you!&lt;/p&gt;&lt;p&gt;If you found this guide helpful in publishing your game, please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email me&lt;/a&gt; so I can play it!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;List of FOSS games inspired by this guide:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.usebox.net/jjm/blog/castaway-source-code/&quot; target=&quot;_blank&quot;&gt;Castaway&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.usebox.net/jjm/blog/the-return-of-traxtor-cpc-source-code/&quot; target=&quot;_blank&quot;&gt;The Return of Traxtor&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Yours?&lt;/li&gt;&lt;/ul&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Open-sourcing-video-games/</link>
        
        <pubDate>Tue, 23 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Open-sourcing-video-games/</guid>
      </item>
    
      <item>
        
        
          <title>We are building a new systems programming language</title>
          <description>
            &lt;p&gt;It’s an open secret: the “secret project” I’ve been talking about is a new systems programming language. It’s been underway since December ’19, and we hope to release the first version in early 2022. The language is pretty small — we have a mostly complete specification which clocks in at 60 pages. It has manual memory management, no runtime, and it uses a superset of the C ABI, making it easy to link with libraries and C code. It should be suitable almost anywhere C is useful: compilers, system utilities, operating systems, network servers and clients, and so on.&lt;/p&gt;&lt;!--
Hello, clever reader :) I was too lazy to make a dedicated build of Hugo with a
fake name for the language. Since you bothered to check, head to
https://harelang.org for the rest of the details. Please keep it to yourself!
--&gt;
&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;type_builtin type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;type_qualifier&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;greetings&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;
		&lt;span class=&quot;string&quot;&gt;&amp;quot;Hello, world!&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;string&quot;&gt;&amp;quot;¡Hola Mundo!&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;string&quot;&gt;&amp;quot;Γειά σου Κόσμε!&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;string&quot;&gt;&amp;quot;Привет мир!&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;string&quot;&gt;&amp;quot;こんにちは世界！&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0z&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant function_call variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;greetings&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant type method_call variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;greetings&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could compare our language to many other languages, but let’s start with how it compares to C:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;More robust error handling via tagged unions&lt;/li&gt;&lt;li&gt;Improved, Unicode-aware string support&lt;/li&gt;&lt;li&gt;Memory safe array, slice, and pointer types (and unsafe versions, if needed)&lt;/li&gt;&lt;li&gt;Direct compatibility with the C ABI for trivial C interop&lt;/li&gt;&lt;li&gt;A simpler, context-free, expression-oriented syntax&lt;/li&gt;&lt;li&gt;A standard library free of the constraints of POSIX or the C standard&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Our language currently supports Linux on x86_64 or aarch64, and we plan on expanding this to the BSDs, Haiku, and Plan 9; as well as i686, riscv64 and riscv32, and ppc64 before the release.&lt;/p&gt;&lt;p&gt;I plan to continue keeping the other details a secret until the release — we want the first release to be a complete, stable, production-ready programming language with all of the trimmings. The first time most people will hear about this language will also be the first time they can ship working code with it.&lt;/p&gt;&lt;p&gt;However, if you want to get involved sooner, there’s a way: we need your help. So far, we’ve written most of the spec, the first of two compilers, and about 15,000 lines of the standard library. The standard library is what needs the most help, and I’m seeking volunteers to get involved.&lt;/p&gt;&lt;p&gt;The standard library mandate begins with the following:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The xxxx standard library shall provide:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Useful features to complement xxxx language features&lt;/li&gt;&lt;li&gt;An interface to the host operating system&lt;/li&gt;&lt;li&gt;Implementations of broadly useful algorithms&lt;/li&gt;&lt;li&gt;Implementations of broadly useful formats and protocols&lt;/li&gt;&lt;li&gt;Introspective meta-features for xxxx-aware programs&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Each of these services shall:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Have a concise and straightforward interface&lt;/li&gt;&lt;li&gt;Correctly and completely implement the useful subset of the required behavior&lt;/li&gt;&lt;li&gt;Provide complete documentation for each exported symbol&lt;/li&gt;&lt;li&gt;Be sufficiently tested to provide confidence in the implementation&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;&lt;p&gt;We have a number of focus areas for standard library development. I expect most contributors, at least at first, to stick to one or two of these areas. The focus areas we’re looking into now are:&lt;/p&gt;&lt;dl&gt;
  &lt;dt&gt;Algorithms&lt;/dt&gt;
  &lt;dd&gt;Sorting • compression • math • etc&lt;/dd&gt;

  &lt;dt&gt;Cryptography&lt;/dt&gt;
  &lt;dd&gt;Hashing • encryption • key derivation • TLS • etc&lt;/dd&gt;

  &lt;dt&gt;Date &amp; time support&lt;/dt&gt;
  &lt;dd&gt;Parsing • formatting • arithmetic • timers • etc&lt;/dd&gt;

  &lt;dt&gt;Debugging tools&lt;/dt&gt;
  &lt;dd&gt;ELF and DWARF support • vDSO • dynamic loading • etc&lt;/dd&gt;

  &lt;dt&gt;Formats &amp; encodings&lt;/dt&gt;
  &lt;dd&gt;JSON • XML • HTML • MIME • RFC 2822 • tar • etc&lt;/dd&gt;

  &lt;dt&gt;&lt;span style=&quot;color: transparent&quot;&gt;xxxx&lt;/span&gt; language support&lt;/dt&gt;
  &lt;dd&gt;Parsing • type checker • hosted toolchain • etc&lt;/dd&gt;

  &lt;dt&gt;Networking&lt;/dt&gt;
  &lt;dd&gt;IP &amp; CIDR handling • sockets • DNS resolver • HTTP • etc&lt;/dd&gt;

  &lt;dt&gt;Platform support&lt;/dt&gt;
  &lt;dd&gt;New platforms and architectures • OS-specific features&lt;/dd&gt;

  &lt;dt&gt;String manipulation&lt;/dt&gt;
  &lt;dd&gt;Search, replace • Unicode • Regex • etc&lt;/dd&gt;

  &lt;dt&gt;Unix support&lt;/dt&gt;
  &lt;dd&gt;chmod • mkfifo • passwd • setuid • TTY management • etc&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;If any of this sounds up your alley, we’d love your help! Please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;write me an email&lt;/a&gt; describing your interest areas and previous systems programming experience.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update 2021-03-20&lt;/strong&gt;: We’re targeting the first release in early 2022, not 2021.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-new-systems-language/</link>
        
        <pubDate>Fri, 19 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-new-systems-language/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, March 2021</title>
          <description>
            &lt;p&gt;After the brief illusion of spring, this morning meets us with a cold apartment indoors and fierce winds outdoors. Today concludes a productive month, mainly for the secret project and for sourcehut, but also marked by progress in some smaller projects as well. I’ll start with those smaller projects.&lt;/p&gt;&lt;p&gt;I have written a feed reader for Gemini, which is (1) &lt;a href=&quot;https://sr.ht/~sircmpwn/gemreader&quot; target=&quot;_blank&quot;&gt;free software&lt;/a&gt;, and (2) &lt;a href=&quot;gemini://feeds.drewdevault.com&quot; target=&quot;_blank&quot;&gt;available as a free hosted service&lt;/a&gt;. Big thanks to adnano, the author of the &lt;a href=&quot;https://sr.ht/~adnano/go-gemini&quot; target=&quot;_blank&quot;&gt;go-gemini&lt;/a&gt; library, which has been very helpful for many of my Gemini-related exploits, and who has been a great collaborator. I also used it to provide Gemini support for the new &lt;a href=&quot;https://srht.site&quot; target=&quot;_blank&quot;&gt;pages.sr.ht&lt;/a&gt;, which offers static web and gemini hosting for sr.ht users. I also updated &lt;a href=&quot;https://sr.ht/~sircmpwn/gmni&quot; target=&quot;_blank&quot;&gt;gmni&lt;/a&gt; to use BearSSL instead of OpenSSL this month.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://godocs.io&quot; target=&quot;_blank&quot;&gt;godocs.io&lt;/a&gt; has been enjoying continued improvements, mainly thanks again to adnano. Heaps of obsolete interfaces and cruft have been excised, not only making it lighter for godocs.io, but also making our &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gddo&quot; target=&quot;_blank&quot;&gt;gddo fork&lt;/a&gt; much easier for you to run yourself. Adnan hopes to have first-class support for Go modules working soon, which will bring us up to feature parity with pkg.go.dev.&lt;/p&gt;&lt;p&gt;There’s some sourcehut news as well, but I’ll leave that for the “What’s cooking” later today. Until next time!&lt;/p&gt;&lt;p&gt;Progress on the secret project has been phenomenal. In the last month, the standard library has doubled in size, and this weekend, we finished the self-hosted build driver. We are about 1,000 lines of code shy of having more code written in Hare than in C.&lt;/p&gt;&lt;p&gt;The call for help last month was swiftly answered, and we have 7 or 8 new people working on the project now. We’ve completed enough work to unblock many workstreams, which will allow these new contributors to work in parallel on different areas of interest, which should substantially speed up progress.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-March-2021/</link>
        
        <pubDate>Mon, 15 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-March-2021/</guid>
      </item>
    
      <item>
        
        
          <title>The corporate surveillance machine is killing people</title>
          <description>
            &lt;p&gt;I have never been angrier about the corporate surveillance complex, which I have rallied against for &lt;em&gt;years&lt;/em&gt;, than I am today. Buying and selling user’s private information on the open market is bad enough for the obvious reasons, but today, I learned that the depths of depravity this market will descend to are without limit. Today I am more angry and ashamed at this industry than I have ever been. Corporate surveillance and adtech has turned your phone into an informant against you and brought about the actual &lt;strong&gt;murder&lt;/strong&gt; of the user.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.vice.com/en/article/y3g97x/location-data-apps-drone-strikes-iowa-national-guard&quot; target=&quot;_blank&quot;&gt;Vice: Military Unit That Conducts Drone Strikes Bought Location Data From Ordinary Apps&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Say you’re a Muslim. You download some apps for reading the Quran and participating in Muslim-oriented social networks. These ads steal whatever personal information it can get its hands on, through any means available, and sell it to &lt;a href=&quot;https://trademarks.justia.com/874/53/locate-87453515.html&quot; target=&quot;_blank&quot;&gt;Locate X&lt;/a&gt;, who stores every GPS location your phone has visited and tags it as being associated with a Muslim. This is used, say, to place Muslim-targeted ads on billboards in Muslim-dense areas. It’s also sold to the Iowa National Guard, who uses it to conduct drone strikes. The app you installed is selling your GPS data so it can be used to &lt;em&gt;kill you&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;For a long time, I have preached “respect the user”. I want us, as programmers, to treat the user with the same standards of common decency and respect we’d afford to our neighbors. It seems I have to revise my sermon to “don’t murder the user”! If you work at a company which surveilles its users, &lt;a href=&quot;https://drewdevault.com/2020/05/05/We-are-complicit-in-our-employers-deeds.html&quot; target=&quot;_blank&quot;&gt;you are complicit&lt;/a&gt; in these murders. You have written software which is used to &lt;em&gt;murder people&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;This industry is in severe need of a moral health check. You, the reader of this article, need to take personal responsibility for what your code is doing. Your boss isn’t going to. Do you really know what that database is being used for, or who it’s being sold to, or who it &lt;em&gt;might&lt;/em&gt; be sold to in the future? Most companies include their hoard of private, personal information about their users as part of their valuation. Do you have stock options, by the way?&lt;/p&gt;&lt;p&gt;I’ve often heard the excuse that employees of large surveillance companies “want to feed their families, like anyone else”. Well, thanks to your work, a child you’ve never met was orphaned, and doesn’t have a family anymore. Who’s going to feed them? Is there really no other way for you to support your family?&lt;/p&gt;&lt;p&gt;Don’t fucking kill your users.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Corporate-surveillance-murder/</link>
        
        <pubDate>Sat, 06 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Corporate-surveillance-murder/</guid>
      </item>
    
      <item>
        
        
          <title>To make money in FOSS, build a business first</title>
          <description>
            &lt;p&gt;I’ve &lt;a href=&quot;https://drewdevault.com/2020/11/20/A-few-ways-to-make-money-in-FOSS.html&quot; target=&quot;_blank&quot;&gt;written about&lt;/a&gt; making money in free and open source software before, but it’s a deep topic that merits additional discussion. While previously I focused on what an individual can do in order to build a career in FOSS, but today I want to talk about how you can build a sustainable business in FOSS.&lt;/p&gt;&lt;p&gt;It’s a common mistake to do this the wrong way around: build the software, then the business. Because FOSS &lt;a href=&quot;https://drewdevault.com/2021/01/20/FOSS-is-to-surrender-your-monopoly.html&quot; target=&quot;_blank&quot;&gt;requires you to surrender your sole monetization rights&lt;/a&gt;, building the software first and worrying about the money later puts you at a huge risk of losing your first-mover advantage. If you’re just making a project which is useful to you and you don’t want the overhead of running a business, then that may be totally okay — you can just build the software without sweating the business issues. If you choose this path, however, be aware that the promise of free and open source software entitles anyone else to build that business without you. If you lapse in your business-building efforts and your software project starts making someone else money, then they’re not at fault for “taking your work” — you gave it to them.&lt;/p&gt;&lt;p&gt;I’ve often said that you can make money in FOSS, but not usually by accident. Don’t just build your project and wait for the big bucks to start rolling in. You need to take the business-building seriously from the start. What is the organization of your company? Who will you work with? What kind of clients or customers will you court? Do you know how to reach them? How much they’re willing to pay? What you will sell? Do you have a budget? If you want to make money from your project, sit down and answer these questions seriously.&lt;/p&gt;&lt;p&gt;Different kinds of software projects make money in different ways. Some projects with enterprise-oriented software may be able to sell support contracts. Some can sell consultants to work on integration and feature development. Maybe you can write books about your software, or teach courses on it. Perhaps your software, like the kind &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;my company&lt;/a&gt; builds, is well-suited to being sold as a service. Some projects simply solicit donations, but this is the most difficult approach.&lt;/p&gt;&lt;p&gt;Whatever you choose to do, you need to choose it deliberately. You need to incorporate your business, hire an accountant, and do a lot of boring stuff which has nothing to do with the software you want to write. And if you skip this step, someone else is entitled to do all of this boring work, then stick your software on top of it and make a killing without you.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/To-make-money-in-FOSS-build-a-business/</link>
        
        <pubDate>Wed, 03 Mar 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/To-make-money-in-FOSS-build-a-business/</guid>
      </item>
    
      <item>
        
        
          <title>Gmail is a huge source of spam</title>
          <description>
            &lt;p&gt;5× as many spam registrations on sourcehut are from gmail than from the second-largest offender.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# SELECT
  SPLIT_PART(email, &amp;apos;@&amp;apos;, 2) as domain, count(*) as count
  FROM &amp;quot;user&amp;quot;
  WHERE user_type = &amp;apos;suspended&amp;apos;
  GROUP BY domain
  ORDER BY count DESC;
          domain           | count
---------------------------+-------
 gmail.com                 |   119
 qq.com                    |    26
 mail.ru                   |    17
 mailinator.com            |    10
 yopmail.com               |     6
 aol.com                   |     6
 yahoo.com                 |     6
[...more omitted...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is just the ones which got through: most spam registrations are detected and ignored before they make it to the database.&lt;/p&gt;&lt;p&gt;A huge number of spam emails I recieve in my personal inbox originate from @gmail.com, and often they arrive in my inbox unscathed (as opposed to going to Junk) because Gmail is considered a reputable mail provider. My colleague estimates that between 15% and 25% of the spam emails sent to a mailing list he administrates comes from Gmail.&lt;/p&gt;&lt;p&gt;One might argue that, because Gmail is the world’s largest email provider, it’s natural to expect that they would have the largest volume of spam simply because they have proportionally more users who might use it for spam. I would argue that this instead tells us that they have the largest responsibility to curtail spam on their platform.&lt;/p&gt;&lt;p&gt;I’ve forwarded many, many reports to &lt;a href=&quot;mailto:abuse@gmail.com&quot; target=&quot;_blank&quot;&gt;abuse@gmail.com&lt;/a&gt;, but they’ve never followed up and the problem has not become any better. I have had half a mind to block Gmail registrations on sourcehut outright, but about 41% of all registrations use Gmail.&lt;/p&gt;&lt;p&gt;It bears repeating that anyone with any level of technical expertise ought to know better than to use Gmail. I usually recommend &lt;a href=&quot;https://www.migadu.com&quot; target=&quot;_blank&quot;&gt;Migadu&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, but there are many options to choose from. If you’re worried about mail deliverability issues, don’t be — it’s more or less a myth in $CURRENTYEAR. If you set up &lt;abbr title=&quot;DomainKeys Identified Mail, an means of verifying message authenticity&quot;&gt;DKIM&lt;/abbr&gt; properly and unlist your IP address from the &lt;abbr title=&quot;DNS blocklists&quot;&gt;DNSBL&lt;/abbr&gt;s (a simple process), then your mails will get through.&lt;/p&gt;&lt;p&gt;In case you’re wondering, the dis-award for second-worst goes to Amazon SES. They don’t register on sourcehut (it’s outgoing only, so that makes sense), but I see them often in my personal inbox. However, SES only appears at a rate of about a tenth of the gmail spam, and they appear to actually listen to my abuse reports, so I can more or less forgive them for it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Gmail-is-a-huge-source-of-spam/</link>
        
        <pubDate>Thu, 25 Feb 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Gmail-is-a-huge-source-of-spam/</guid>
      </item>
    
      <item>
        
        
          <title>A great alternative is rarely fatter than what it aims to replace</title>
          <description>
            &lt;p&gt;This is not always true, but in my experience, it tends to hold up. We often build or evaluate tools which aim to replace something kludgy^Wvenerable. Common examples include shells, programming languages, system utilities, and so on. Rust, Zig, etc, are taking on C in this manner; so too does zsh, fish, and oil take on bash, which in turn takes on the Bourne shell. There are many examples.&lt;/p&gt;&lt;p&gt;All of these tools are fine in their own respects, but they have all failed to completely supplant the software they’re seeking to improve upon.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; What these projects have in common is that they &lt;em&gt;expand&lt;/em&gt; on the ideas of their predecessors, rather than &lt;em&gt;refining&lt;/em&gt; them. A truly great alternative finds the nugget of truth at the center of the idea, cuts out the cruft, and solves the same problem with less.&lt;/p&gt;&lt;p&gt;This is one reason I like Alpine Linux, for example. It’s not really aiming to replace any distro in particular so much as it competes with the Linux ecosystem as a whole. Alpine does this by being &lt;em&gt;simpler&lt;/em&gt; than the rest: it’s the only Linux system I can fit more or less entirely in my head. Compare this to the most common approach: “let’s make a Debian derivative!” It kind of worked for Ubuntu, less so for everyone else. The C library Alpine ships, &lt;a href=&quot;https://musl.libc.org&quot; target=&quot;_blank&quot;&gt;musl libc&lt;/a&gt;, is another example: it aims to replace glibc by being leaner and meaner, &lt;a href=&quot;https://drewdevault.com/2020/09/25/A-story-of-two-libcs.html&quot; target=&quot;_blank&quot;&gt;and I’ve talked about its success in this respect before&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Go is a programming language which has done relatively well in this respect. It aimed to fill a bit of a void in the high-performance internet infrastructure systems programming niche,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; and it is markedly simpler than most of the other tools in its line of work. It takes the opportunity to add a few innovations — its big risk is its novel concurrency model — but Go balances this with a level of simplicity in other respects which is unchallenged among its contemporaries,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; and a commitment to that simplicity which has endured for years.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;There are many other examples. UTF-8 is a simple, universal approach which smooths over the idiosyncrasies of the encoding zoo which pre-dates it, and has more-or-less rendered its alternatives obsolete. JSON has almost completely replaced XML, and its grammar famously fits on a business card.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; On the other hand, when zsh started as a superset of bash, it crippled its ability to compete on “having less warts than bash”.&lt;/p&gt;&lt;p&gt;Rust is more vague in its inspirations, and does not start as a superset of anything. It has, however, done a poor job of scope management, and is significantly more complex than many of the languages it competes with, notably C and Go. For this reason, it struggles to root out the hold-outs in those domains, and it suffers for the difficulty in porting it to new platforms, which limits its penetration into a lot of domains that C is still thriving in. However, it succeeds in being much simpler than C++, and I expect that it will render C++ obsolete in the coming years as such.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fn-7-ref-1&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;In computing, we make do with a hodge podge of hacks and kludges which, at best, approximate the solutions to the problems that computing presents us. If you start with one such hack as the basis of a supposed replacement and build &lt;em&gt;more&lt;/em&gt; on top of it, you will inherit the warts, and you may find it difficult to rid yourself of them. If, instead, you question the premise of the software, interrogate the underlying problem it’s trying to solve, and apply your insights, plus a healthy dose of hindsight, you may isolate what’s right from what’s superfluous, and your simplified solution just might end up replacing the cruft of yore.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/On-the-traits-of-good-replacements/</link>
        
        <pubDate>Sun, 21 Feb 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/On-the-traits-of-good-replacements/</guid>
      </item>
    
      <item>
        
        
          <title>How to make your downstream users happy</title>
          <description>
            &lt;p&gt;There are a number of things that your FOSS project can be doing which will make the lives of your downstream users easier, particularly if you’re writing a library or programmer-facing tooling. Many of your downstreams (Linux distros, pkgsrc, corporate users, etc) are dealing with lots of packages, and some minor tweaks to your workflow will help them out a lot.&lt;/p&gt;&lt;p&gt;The first thing to do is &lt;em&gt;avoid&lt;/em&gt; using any build system or packaging system which is not the norm for your language. Also avoid incorporating information into your build which relies on being in your git repo — most packagers prefer to work with tarball snapshots, or to fetch your package from e.g. PyPI. These two issues are definitely the worst offenders. If you do have to use a custom build system, take your time to document it thoroughly, so that users who run into problems are well-equipped to address them. The typical build system or packaging process in use for your language already addressed most of those edge cases long ago, which is why we like it better. If you must fetch, say, version information from git, then please add a fallback, such as an environment variable.&lt;/p&gt;&lt;p&gt;Speaking of environment variables, another good one to support is &lt;a href=&quot;https://reproducible-builds.org/docs/source-date-epoch/&quot; target=&quot;_blank&quot;&gt;SOURCE_DATE_EPOCH&lt;/a&gt;, for anything where the current date or time is incorporated into your build output. Many distros are striving for &lt;em&gt;reproducible&lt;/em&gt; builds these days, which involves being able to run a build twice, or by two independent parties, and arrive at an identical checksum-verifiable result. You can probably imagine some other ways to prevent issues here — don’t incorporate the full path to each file in your logs, for instance. There are more recommendations on the website linked earlier.&lt;/p&gt;&lt;p&gt;Though we don’t like to rely on it as part of the formal packaging process, a good git discipline will also help us with the informal parts. You may already be using &lt;a href=&quot;https://git-scm.com/docs/git-tag&quot; target=&quot;_blank&quot;&gt;git tags&lt;/a&gt; for your releases — consider putting a changelog into your annotated tags (git tag -a). If you have &lt;a href=&quot;https://drewdevault.com/2019/02/25/Using-git-with-discipline.html&quot; target=&quot;_blank&quot;&gt;good commit discipline&lt;/a&gt; in your project, then you can easily use &lt;a href=&quot;https://git-scm.com/docs/git-shortlog&quot; target=&quot;_blank&quot;&gt;git shortlog&lt;/a&gt; to generate such a changelog from your commit messages. This helps us understand what we can expect when upgrading, which helps incentivize us to upgrade in the first place. In &lt;a href=&quot;https://drewdevault.com/2019/10/12/how-to-fuck-up-releases.html&quot; target=&quot;_blank&quot;&gt;How to fuck up software releases&lt;/a&gt;, I wrote about my &lt;a href=&quot;https://git.sr.ht/~sircmpwn/dotfiles/tree/master/bin/semver&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt; tool, which you may find helpful in automating this process. It can also help you avoid forgetting to do things like update the version number somewhere in the code.&lt;/p&gt;&lt;iframe src=&quot;https://asciinema.org/a/nzBvuMXjUMsoewLnrbTm7E28O/embed?&quot; id=&quot;asciicast-iframe-nzBvuMXjUMsoewLnrbTm7E28O&quot; name=&quot;asciicast-iframe-nzBvuMXjUMsoewLnrbTm7E28O&quot; scrolling=&quot;no&quot; allowfullscreen=&quot;true&quot; style=&quot;overflow: hidden; border: 0px; width: 540px; float: none; visibility: visible; height: 404px;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;In short, to make your downstreams happy:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Don’t rock the boat on builds and packaging.&lt;/li&gt;&lt;li&gt;Don’t expect your code to always be in a git repo.&lt;/li&gt;&lt;li&gt;Consider reproducible builds.&lt;/li&gt;&lt;li&gt;Stick a detailed changelog in your annotated tag — which is easy if you have good commit discipline.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Overall, this is pretty easy stuff, and good practices which pay off in other respects as well. Here’s a big “thanks” in advance from your future downstreams for your efforts in this regard!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-make-your-downstreams-happy/</link>
        
        <pubDate>Tue, 09 Feb 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-make-your-downstreams-happy/</guid>
      </item>
    
      <item>
        
        
          <title>Use open platforms — or else</title>
          <description>
            &lt;p&gt;The ongoing events around &lt;a href=&quot;https://old.reddit.com/r/wallstreetbets&quot; target=&quot;_blank&quot;&gt;/r/wallstreetbets&lt;/a&gt; teaches us, once again, about the value of open platforms, and the tremendous &lt;em&gt;risk&lt;/em&gt; involved in using proprietary platforms. The economic elites who control those proprietary platforms, backed by their venture capital interests, &lt;em&gt;will&lt;/em&gt; shut us down if we threaten them. We’re taking serious risk by casting our lot with them.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Discord_(software)&quot; target=&quot;_blank&quot;&gt;Discord&lt;/a&gt;, a proprietary instant messaging and VoIP platform, kicked out the /r/WSB community yesterday. They claimed it was due to spam and abuse from bots. These are convenient excuses when considered in the broader context of Discord’s conflict of interest, between its retail investor users and its wall-street investor backers. However, even if we take their explanation at face value, we can easily question Discord’s draconian policies about its proprietary chat protocol. They have a history of cracking down on third-party bots and clients with the same excuses of preventing spam and abuse. If Discord accepts responsibility for preventing spam and abuse, then why are they deplatforming users when they, Discord, failed to prevent it?&lt;/p&gt;&lt;p&gt;It’s all a lie. They use a proprietary protocol and crack down on third-party implementations because they demand total control over their users. They deplatformed /r/WSB because they were financially threatened by them. Discord acts in their own interests, including when they are against the interests of their users. In the words of Rohan Kumar, they’re trying to &lt;a href=&quot;https://seirdy.one/2021/01/27/whatsapp-and-the-domestication-of-users.html&quot; target=&quot;_blank&quot;&gt;domesticate their users&lt;/a&gt;. It’s the same with every corporate-operated platform. Betting that Reddit will ultimately shut down /r/WSB is probably a stronger bet than buying GME!&lt;/p&gt;&lt;p&gt;But there is another way: free and open platforms, protocols, and standards. Instead of Discord, I could recommend &lt;a href=&quot;https://matrix.org&quot; target=&quot;_blank&quot;&gt;Matrix&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Relay_Chat&quot; target=&quot;_blank&quot;&gt;IRC&lt;/a&gt;, or &lt;a href=&quot;https://www.mumble.info&quot; target=&quot;_blank&quot;&gt;Mumble&lt;/a&gt;. These are not based on central corporate ownership, but instead on publicly available standards that anyone can build on top of. The ownership of these platforms is distributed between its users, and thus aligned with their incentives.&lt;/p&gt;&lt;p&gt;Federation is also a really compelling solution. Unlike Discord and Reddit, which are centrally owned and operated, federated software calls for many independent server operators to run instances which are responsible for tens or hundreds of users each. Each of these servers then use standardized protocols to communicate with each other, forming one cohesive, distributed social network. Matrix and IRC are both federated protocols, for example. Others include &lt;a href=&quot;https://joinmastodon.org/&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;, which is similar to Twitter in function; &lt;a href=&quot;https://joinpeertube.org/en&quot; target=&quot;_blank&quot;&gt;PeerTube&lt;/a&gt;, for hosting videos and live streams; and &lt;a href=&quot;https://join.lemmy.ml/&quot; target=&quot;_blank&quot;&gt;Lemmy&lt;/a&gt;, which is a federated equivalent of Reddit.&lt;/p&gt;&lt;p&gt;These are the alternatives. These platforms lack that crucial conflict of interest which is getting us kicked off of the corporate owned platforms. These are the facts: open platforms are the only ones align with the interests of their users, and closed platforms exploit their users. Once you recognize this, you should jump ship &lt;em&gt;before&lt;/em&gt; you’re deplatformed, or else you’re risking your ability to organize yourselves to move to another platform. Use open platforms — or else. Do it today.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Use-open-platforms-or-else/</link>
        
        <pubDate>Thu, 28 Jan 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Use-open-platforms-or-else/</guid>
      </item>
    
      <item>
        
        
          <title>Open source means surrendering your monopoly over commercial exploitation</title>
          <description>
            &lt;p&gt;Participation in open source requires you to surrender your monopoly over commercial exploitation. This is a profound point about free and open source software which seems to be causing a lot of companies to struggle with their understanding of the philosophy of FOSS, and it’s worth addressing on its own. It has been apparent for some years now that FOSS is eating the software world, and corporations are trying to figure out their relationship with it. One fact that you will have to confront in this position is that you cannot monopolize the commercial potential of free and open source software.&lt;/p&gt;&lt;p&gt;The term “open source” is broadly accepted as being defined by the &lt;a href=&quot;https://opensource.org/osd&quot; target=&quot;_blank&quot;&gt;Open Source Definition&lt;/a&gt;, and its very first requirement is the following:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[The distribution terms of open-source software] shall not restrict any party from selling or giving away the software as a component of an aggregate software distribution containing programs from several different sources. The license shall not require a royalty or other fee for such sale.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;That covers the “OSS” in “FOSS”. The “F” refers to “free software”, and is covered by &lt;a href=&quot;https://www.gnu.org/philosophy/free-sw.html&quot; target=&quot;_blank&quot;&gt;this Free Software Foundation resource&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[A program is free software if the program’s users have] the freedom to run the program as they wish, for any purpose, [… and to …] redistribute copies.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It further clarifies the commercial aspect of this freedom explicitly:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;“Free software” does not mean “noncommercial”. A free program must be available for commercial use, commercial development, and commercial distribution. […] Regardless of how you got your copies, you always have the freedom to copy and change the software, [and] to sell copies.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This is an essential, non-negotiable requirement of free and open-source software, and a reality you must face if you want to reap the benefits of the FOSS ecosystem. &lt;em&gt;Anyone&lt;/em&gt; can monetize your code. That includes you, and me, all of your contributors, your competitors, Amazon and Google, and everyone else. This is a rejection of how intellectual property typically works — copyright laws exist for the express purpose of creating an artificial monopoly for your business, and FOSS licenses exist for the express purpose of breaking it. If you’re new to FOSS, it is going to be totally alien to your understanding of IP ownership.&lt;/p&gt;&lt;p&gt;It’s quite common for people other than you to make money from your free and open source software works. Some will incorporate them into their own products to sell, some will develop an expertise with it and sell their skills as a consultant, some will re-package it in an easy-to-use fashion and charge people for the service. Others might come up with even more creative ways to monetize the software, like writing books about it. It will create wealth for everyone, not just the original authors. And if you want it to create wealth for &lt;em&gt;you&lt;/em&gt;, you are responsible for figuring out how. Building a business requires more work than just writing the software.&lt;/p&gt;&lt;p&gt;This makes sense in terms of karmic justice, as it were. One of the most important advantages of making your software FOSS is that the global community can contribute improvements back to it. The software becomes more than your organization can make it alone, both through direct contributions to your code, and through the community which blossoms around it. If the sum of its value is no longer entirely accountable to your organization, is it not fair that the commercial exploitation of that value shouldn’t be entirely captured by your organization, either? This is the deal that you make when you choose FOSS.&lt;/p&gt;&lt;p&gt;There are ways that you can influence how others use your FOSS software, mainly having to do with making sure that everyone else keeps this same promise. You cannot stop someone from making money from your software, but you &lt;em&gt;can&lt;/em&gt; obligate them to share their improvements with everyone else, which you can incorporate back into the original product to make it more compelling for everyone. The &lt;a href=&quot;https://www.gnu.org/licenses/&quot; target=&quot;_blank&quot;&gt;GPL family of licenses&lt;/a&gt; is designed for this purpose.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Furthermore, if your business is a consumer of free and open source software, rather than a producer, you need to be aware that you may be subject to those obligations. It’s not a free lunch: you may be required to return your improvements to the community. FOSS licenses are important, and you should make it your business to understand them, both as a user, contributor, and author of free and open source software.&lt;/p&gt;&lt;p&gt;FOSS is eating the world, and it’s a very attractive choice for businesses for a good reason. This is the reason. It increases wealth for everyone. Capitalism concerns itself with making monopolies — FOSS instead concerns itself with the socialized creation of software wealth.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/FOSS-is-to-surrender-your-monopoly/</link>
        
        <pubDate>Wed, 20 Jan 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/FOSS-is-to-surrender-your-monopoly/</guid>
      </item>
    
      <item>
        
        
          <title>Spooky action at a distance</title>
          <description>
            &lt;p&gt;Einstein famously characterized the strangeness of quantum mechanics as “spooky action at a distance”, which, if I had to pick one phrase about physics to be my favorite, would be a strong contender. I like to relate this to programming language design: there are some language features which are similarly spooky. Perhaps the most infamous of these is operator overloading. Consider the following:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;x + y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If this were written in C, without knowing anything other than the fact that this code compiles correctly, I can tell you that x and y are numeric types, and the result is their sum. I can even make an educated guess about the CPU instructions which will be generated to perform this task. However, if this were a language with operator overloading… who knows? What if x and y are some kind of some Vector class? It could compile to this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Vector::operator_plus(x, y)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The performance characteristics, consequences for debugging, and places to look for bugs are considerably different than the code would suggest on the surface. This function call is the “spooky action” — and the distance between the “+” operator and the definition of its behavior is the “distance”.&lt;/p&gt;&lt;p&gt;Also consider if x and y are strings: maybe “+” means concatenation? Concatenation often means allocation, which is a pretty important side-effect to consider. Are you going to thrash the garbage collector by doing this? &lt;em&gt;Is&lt;/em&gt; there a garbage collector, or is this going to leak? Again, using C as an example, this case would be explicit:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;char *new = malloc(strlen(x) + strlen(y) + 1);
strcpy(new, x);
strcat(new, y);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the filename of the last file you had open in your text editor ended in &lt;code&gt;.rs&lt;/code&gt;, you might be frothing at the mouth after reading this code. Strictly for the purpose of illustrating my point, however, consider that everything which happens here is explicit, opt-in to the writer, and obvious to the reader.&lt;/p&gt;&lt;p&gt;That said, C doesn’t get off scott-free in this article. Consider the following code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;int x = 10, y = 20;
int z = add(x, y);
printf(&amp;quot;%d + %d = %d\n&amp;quot;, x, y, z);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You may expect this to print out &lt;code&gt;10 + 20 = 30&lt;/code&gt;, and you would be forgiven for your naivety.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ cc -o test test.c
$ ./test
30 + 20 = 30
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The savvy reader may have already figured out the catch: add is not a function.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#define add(x, y) x += y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The spooky action is the mutation of x, and the distance is between the apparent “callsite” and the macro definition. This is spooky because it betrays the reader’s expectations: it looks and smells like a function call, but it does something which breaks the contract of function calls. Some languages do this better, by giving macros an explicit syntax like &lt;code&gt;name!(args...)&lt;/code&gt;, but, personally, I still don’t like it.&lt;/p&gt;&lt;p&gt;Language features like this are, like all others, a trade-off. But I’m of the opinion that this trade is unwise: you’re wagering readability, predictability, debuggability, and more. These features are toxic to anyone seeking stable, robust code. They certainly have no place in systems programming.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Spooky-code-at-a-distance/</link>
        
        <pubDate>Tue, 19 Jan 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Spooky-code-at-a-distance/</guid>
      </item>
    
      <item>
        
        
          <title>Elasticsearch does not belong to Elastic</title>
          <description>
            &lt;p&gt;Elasticsearch belongs to its 1,573 contributors, who retain their copyright, and granted Elastic a license to distribute their work without restriction. This is the loophole which Elastic exploited when they decided that Elasticsearch &lt;a href=&quot;https://www.elastic.co/blog/licensing-change&quot; target=&quot;_blank&quot;&gt;would no longer be open source&lt;/a&gt;, a loophole that they introduced with this very intention from the start. When you read their announcement, don’t be gaslit by their deceptive language: Elastic is no longer open source, and this is a move against open source. It is not “doubling down on open”. &lt;strong&gt;Elastic has spit in the face of every single one of 1,573 contributors, and everyone who gave Elastic their trust, loyalty, and patronage&lt;/strong&gt;. This is an Oracle-level move.&lt;/p&gt;&lt;iframe
  width=&quot;560&quot;
  height=&quot;315&quot;
  src=&quot;https://www.youtube-nocookie.com/embed/-zRN7XLCRhc?start=2483&quot;
  frameborder=&quot;0&quot;
  allow=&quot;accelerometer; autoplay; gyroscope; picture-in-picture&quot;
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;
&lt;a
  style=&quot;display: block; text-align: center&quot;
  href=&quot;https://youtu.be/-zRN7XLCRhc?t=2483&quot;
&gt;&lt;small&gt;Bryan Cantrill on OpenSolaris &amp;mdash; YouTube&lt;/small&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Many of those contributors were there because they believe in open source. Even those who work for Elastic as their employees, who had their copyright taken from them by their employer, work there because they believe in open source. I am frequently asked, “&lt;a href=&quot;https://drewdevault.com/2020/11/20/A-few-ways-to-make-money-in-FOSS.html&quot; target=&quot;_blank&quot;&gt;how can I get paid to work in open source&lt;/a&gt;”, and one of my answers is to recommend a job at companies like Elastic. People seek these companies out because they want to be involved in open source.&lt;/p&gt;&lt;p&gt;Elastic was not having their lunch eaten by Amazon. They cleared half a billion dollars last year. Don’t gaslight us. Don’t call your product “free &amp; open”, deliberately misleading users by aping the language of the common phrase “free &amp; open source”. You did this to get even more money, you did it to establish a monopoly over Elasticsearch, and you did it in spite of the trust your community gave you. Fuck you, Shay Banon.&lt;/p&gt;&lt;p&gt;I hope everyone reading will remember this as yet another lesson in the art of &lt;a href=&quot;https://drewdevault.com/2018/10/05/Dont-sign-a-CLA.html&quot; target=&quot;_blank&quot;&gt;never signing a CLA&lt;/a&gt;. Open source is a community endeavour. It’s a committment to enter your work into the commons, and to allow the community to collectively benefit from it — even financially. Many people built careers and businesses out of Elasticsearch, independently of Elastic, and were entitled to do so under the social contract of open source. Including Amazon.&lt;/p&gt;&lt;p&gt;You don’t own it. Everyone owns it. This is &lt;em&gt;why open source is valuable&lt;/em&gt;. If you want to play on the FOSS playing field, then you play by the goddamn rules. If you aren’t interested in that, then you’re not interested in FOSS. You’re free to distribute your software any way you like, including under proprietary or source-available license terms. But if you choose to make it FOSS, that means something, and you have the moral obligation to uphold.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Elasticsearch-does-not-belong-to-Elastic/</link>
        
        <pubDate>Tue, 19 Jan 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Elasticsearch-does-not-belong-to-Elastic/</guid>
      </item>
    
      <item>
        
        
          <title>History will not remember us fondly</title>
          <description>
            &lt;p&gt;Today, we recall the Middle Ages as an unenlightened time (quite literally, in fact). We view the Middle Ages with a critical eye towards its brutality, lack of individual freedoms, and societal and technological regression. But we rarely turn that same critical lens on ourselves to consider how we’ll be perceived by future generations. I expect the answer, upsetting as it may be, is this: the future will think poorly of us.&lt;/p&gt;&lt;p&gt;We possess the resources and production necessary to provide every human being on Earth with a comfortable living: adequate food, housing, health, and happiness. We have decided not to do so. We have achieved what one may consider the single unifying goal of the entire history of humanity: we have eliminated natural scarcity for our basic resources. We have done this, and we choose to deny our fellow humans their basic needs, in the cruel pursuit of profit. We have more empty homes than we have homeless people. America alone throws away enough food to feed the entire world population. And we choose to let our peers die of hunger and exposure.&lt;/p&gt;&lt;p&gt;We are politically destitute. Profits again drive everything — in the United States, Citizens United gave corporations unfettered access to buy and sell political will, and in the time since they have successfully installed politicians favorable to the elite class. Our corporations possess obscene wealth, coffers that rival those of nation-states, and rule over our people via their proxies in political office. Princeton published &lt;a href=&quot;https://www.cambridge.org/core/journals/perspectives-on-politics/article/testing-theories-of-american-politics-elites-interest-groups-and-average-citizens/62327F513959D0A304D4893B382B992B&quot; target=&quot;_blank&quot;&gt;a study&lt;/a&gt; in 2014 which showed that the opinions of the average American citizen has a statistically negligible effect on political outcomes, while the opinions of the elite can all but decide the same outcomes. Our capitalist owners have unchallenged rule over society, and they rule it with the single-minded obsession to create profit at any cost, including lives.&lt;/p&gt;&lt;p&gt;The US Capitol was overrun by armed seditionists yesterday. Armed seditionists, who, by the way, were radicalized on the internet. As a computer engineer, I am complicit in this radicalization. The early internet was a sea of optimism, full of enthusiasm about the growing connectivity between people which had the potential to unite humanity like never before. We early adopters felt like world citizens: making friends, collaborating, and uniting with no respect for borders or ideology.  What we hadn’t realized is that we were also building the most powerful tool the world has ever seen for censorship, propaganda, and radicalization.&lt;/p&gt;&lt;p&gt;The companies which built this technology are modern slave drivers, broadly eroding worker freedoms in the first world, and in the third world seeking to exploit the cheapest slave labor they can find. We are developing technology which facilitates the authoritarian and &lt;em&gt;genocidal&lt;/em&gt; policies of China. Anyone who speaks out is fired, corrections are quickly issued, and a statement of unconditional support for the profit generating, population murdering thugs is proclaimed. I speak passionately to my peers in my field, begging them to fight back, but many lack the courage, and most don’t care — so long as their exorbitant paychecks keep coming in. Money, money, money. We are at one end of a process which launders money to wash off the blood. Morals are dead.&lt;/p&gt;&lt;p&gt;It’s not just America — democracy is on the decline world-wide. A friend in France recently took to the streets to protest against the introduction of laws protecting the police from citizen oversight. Populist traitors tore the UK out of the EU, effective last week, dooming their people to economic and political destitution. The Greek economy has failed, right-wingers are passing discriminatory laws against LGBT Poles, and conservative populism has taken hold of much of Italy, just to name a few more. Social and political systems are regressing worldwide.&lt;/p&gt;&lt;a href=&quot;https://redacted.moe/f/85f7d261.png&quot;&gt;
  &lt;img
    style=&quot;max-width: 100%&quot;
    alt=&quot;A visualization of democratic decline across Europe and Eurasia, showing that declines have outweighed gains in each of the past 10 years, demonstrating a persistent net decline in democracy.&quot;
    src=&quot;https://redacted.moe/f/85f7d261.png&quot;&gt;
&lt;/a&gt;

&lt;small style=&quot;display: block; text-align: center&quot;&gt;Source: &lt;a href=&quot;https://freedomhouse.org/report/nations-transit/2020/dropping-democratic-facade&quot;&gt;Freedom House&lt;/a&gt;&lt;/small&gt;
&lt;p&gt;Our entire society boils down to one measure: profit. We are being eaten alive by capitalism. Americans have been brainwashed into a national ethos which is &lt;em&gt;defined&lt;/em&gt; by capitalism. In the relentless pursuit of profits, we have eroded all political and social freedoms and created a system defined by its remarkable cruelty in a time when we have access to greater wealth and resources than at any other time in history.&lt;/p&gt;&lt;p&gt;Perhaps future generations won’t remember us after all, considering that in that same relentless pursuit of profits we are vigorously rendering the Earth uninhabitable. But, if they do live to remember us, they will remember us as a wicked, cruel, and unempathetic lot. We will be remembered in disgrace.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/History-will-not-remember-us-fondly/</link>
        
        <pubDate>Thu, 07 Jan 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/History-will-not-remember-us-fondly/</guid>
      </item>
    
      <item>
        
        
          <title>Fostering a culture that values stability and reliability</title>
          <description>
            &lt;p&gt;There’s an idea which encounters a bizarre level of resistance from the broader software community: that software can be completed. This resistance manifests in several forms, perhaps the most common being the notion that a git repository which doesn’t receive many commits is abandoned or less worthwhile. For my part, I consider software that aims to be &lt;em&gt;completed&lt;/em&gt; to be more worthwhile most of the time.&lt;/p&gt;&lt;p&gt;There are two sources of change which projects are affected by: external and internal. An internal source of change is, for example, a planned feature, or a discovered bug. External sources of change are, say, when a dependency makes a breaking change and your software has to be updated accordingly. Some projects will necessarily have an indefinite source of external change to consider, often as part of their value proposition. &lt;a href=&quot;https://youtube-dl.org/&quot; target=&quot;_blank&quot;&gt;youtube-dl&lt;/a&gt; will always evolve to add new sites and workarounds, &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt; will continue to grow to take advantage of new graphics and input hardware features, and so on.&lt;/p&gt;&lt;p&gt;Any maintained program will naturally increase in stability over time as bug fixes accumulate, towards some finite maximum. However, change drives this trend in reverse. Introducing new features, coping with external change factors, even fixing bugs, all of this often introduce new problems. If you want to produce software which is reliable, robust, and stable, then managing change is an essential requirement.&lt;/p&gt;&lt;p&gt;To this end, software projects can, and often should, draw a finish line. Or, if not a finish line, a curve for gradually backing off on feature introduction, raising the threshold of importance by which a new feature is considered.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/swaywm/sway&quot; target=&quot;_blank&quot;&gt;Sway&lt;/a&gt;, for instance, was “completed” some time ago. We stopped accepting most major feature requests, preferring only to implement changes which were made necessary by external sources: notably, features implemented in i3, the project sway aimed to replace. The i3 project &lt;a href=&quot;https://old.reddit.com/r/i3wm/comments/kn8pa2/an_update_on_the_future_of_i3/&quot; target=&quot;_blank&quot;&gt;announced this week&lt;/a&gt; that it was adopting a similar policy regarding new features, and thus sway’s change management is again reduced in scope to only addressing bugs and performance. Sway has completed its value proposition, and now our only goal is to become more and more stable and reliable at delivering it.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/scdoc&quot; target=&quot;_blank&quot;&gt;scdoc&lt;/a&gt; is another project which has met its stated goals. Its primary external source of change is roff — which is almost 50 years old. Therefore, it has accumulated mainly bugfixes and robustness over the past few years since its release, and users enjoy a great deal of reliability and stability from it. Becoming a tool which “just works” and can be depended on without a second thought is the only goal going forward.&lt;/p&gt;&lt;p&gt;Next time you see a git repo which is only getting a slow trickle of commits, don’t necessarily write it off as abandoned. A slow trickle of commits is the ultimate fate of software which aims to be stable and reliable. And, as a maintainer of your own projects, remember that turning a critical eye to new feature requests, and evaluating their cost in terms of complexity and stability, is another responsibility that your users are depending on you for.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-culture-of-stability-and-reliability/</link>
        
        <pubDate>Mon, 04 Jan 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-culture-of-stability-and-reliability/</guid>
      </item>
    
      <item>
        
        
          <title>A megacorp is not your dream job</title>
          <description>
            &lt;p&gt;Megacorporations&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;em&gt;do not&lt;/em&gt; care about you. You’re worth nothing to them. Google made $66 billion in 2014 — even if you made an exorbitant $500K salary, you only cost them .00075% of that revenue. They are not invested in you. Why should you invest in them? Why should you give a company that isn’t invested in you 40+ hours of your week, half your waking life, the &lt;em&gt;only&lt;/em&gt; life you get?&lt;/p&gt;&lt;p&gt;You will have little to no meaningful autonomy, impact, or influence. Your manager’s manager’s manager’s manager (1) will exist, and (2) will not know your name, and probably not your manager’s name either. The company will be good at advertising their jobs, especially to fresh grads, and you will no doubt have dozens of cool project in mind that you’re itching to get involved with. You won’t be assigned any of them — all vacancies are already filled by tenured staff and nepotism. You’re more likely to work on a product you have hardly ever heard of or used, doing work that doesn’t interest you or meaningfully impact anyone you know.&lt;/p&gt;&lt;p&gt;A business doesn’t get a billion-dollar valuation (or… ugh… a trillion-dollar valuation) by having a productive team which takes good care of its employees, rewarding them with interesting projects, or quickly correcting toxic work environments. A business might get millions of dollars, at most, with that approach. The megacorps got their 10th figure with another strategy: ruthlessness. They create and exploit monopolies, and bribe regulators to look the other way. They acquire and dismantle competitors. They hire H1B’s and subject them to payroll fraud and workplace abuse, confident that they can’t quit without risking their visa. Megacorps are a faceless machine which is interested only in making as much money as possible with any resources at their disposal, among those being a budget which exceeds most national GDPs.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;If anything goes wrong in this heartless environment, you’re going to be in a very weak position. If you go to HR&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; for almost any dispute, they are unlikely to help. If you quit, remember that they will have forced you to sign an NDA and a non-compete.  You’re rolling the dice on whether or not they’ll decide that you’ve overstepped (and they can &lt;em&gt;decide&lt;/em&gt; that — the terms are virtually impossible not to breach). That .00075% of their annual revenue you took home? They could easily spend 100x that on lawyers without breaking a sweat, and money is justice in the United States. You will likely have no recourse if they wrong you.&lt;/p&gt;&lt;p&gt;They may hurt you, but even worse, they will make you hurt others. You will be complicit in their ruthlessness. Privacy dragnets, union busting, monopolistic behavior and lobbying, faux-slavery of gig workers in domestic warehouses and actual-slavery of workers in foreign factories, answering to &lt;a href=&quot;https://en.wikipedia.org/wiki/Uyghur_genocide&quot; target=&quot;_blank&quot;&gt;nations committing actual ongoing genocide&lt;/a&gt; — this is only possible because highly skilled individuals like yourself chose to work for them, build their war chest, or even directly contribute to these efforts. Your salary may be a drop in the bucket to them, but consider how much that figure means to you. If you make that $500K, they spend 1.5× that after overhead, and they’d only do it if they expect a return on that investment. Would you give a corporation with this much blood on its hands $750K of your worth? Pocket change to them, maybe, but a lot of value to &lt;em&gt;you&lt;/em&gt;, value that you could be adding somewhere else.&lt;/p&gt;&lt;p&gt;They won’t care about you. They won’t be invested in you. They won’t give you interesting work. You will have no recourse if things go wrong, and things are primed to go wrong. They could hurt you, and they could make you hurt others. Don’t fall for their propaganda.&lt;/p&gt;&lt;p&gt;Megacorps are, in fact, in the minority. There are tens of thousands of other tech companies that could use your help. Tech workers are in high demand — you have choices! You will probably be much happier at a small to mid-size company. The “dream job” megacorps have sold you on is just good marketing.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Megacorps-are-not-your-dream-job/</link>
        
        <pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Megacorps-are-not-your-dream-job/</guid>
      </item>
    
      <item>
        
        
          <title>How to design a new programming language from scratch</title>
          <description>
            &lt;p&gt;There is a long, difficult road from vague, pie-in-the-sky ideas about what would be cool to have in a new programming language, to a robust, self-consistent, practical implementation of those ideas. Designing and implementing a new programming language from scratch is one of the most challenging tasks a programmer can undertake.&lt;/p&gt;&lt;p&gt;Note: this post is targeted at motivated programmers who want to make a serious attempt at designing a useful programming language. If you just want to make a language as a fun side project, then you can totally just wing it. Taking on an unserious project of that nature is also a good way to develop some expertise which will be useful for a serious project later on.&lt;/p&gt;&lt;p&gt;Let’s set the scene. You already know a few programming languages, and you know what you like and dislike about them — these are your influences. You have some cool novel language design ideas as well. A good first step from here is to dream up some pseudocode, putting some of your ideas to paper, so you can get an idea of what it would actually feel like to write or read code in this hypothetical language. Perhaps a short write-up or a list of goals and ideas is also in order. Circulate these among your confidants for discussion and feedback.&lt;/p&gt;&lt;p&gt;Ideas need to be proven in the forge of implementations, and the next step is to write a compiler (or interpreter — everything in this article applies equally to them). We’ll call this the sacrificial implementation, because you should be prepared to throw it away later. Its purpose is to prove that your design ideas work and can be implemented efficiently, but &lt;em&gt;not&lt;/em&gt; to be the production-ready implementation of your new language. It’s a tool to help you refine your language design.&lt;/p&gt;&lt;p&gt;To this end, I would suggest using a parser generator like yacc to create your parser, even if you’d prefer to ultimately use a different design (e.g. recursive descent). The ability to quickly make changes to your grammar, and the side-effect of having a formal grammar written as you work, are both valuable to have at this stage of development. Being prepared to throw out the rest of the compiler is helpful because, due to the inherent difficulty of designing and implementing a programming language at the same time, your first implementation will probably be shit. You don’t know what the language will look like, you’ll make assumptions that you have to undo later, and it’ll undergo dozens of refactorings. It’s gonna suck.&lt;/p&gt;&lt;p&gt;However, shit as it may be, it will have done important work in validating your ideas and refining your design. I would recommend that your next step is to start working on a formal specification of the language (something that I believe all languages should have). You’ve proven what works, and writing it up formally is a good way to finalize the ideas and address the edge cases. Gather a group of interested early adopters, contributors, and subject matter experts (e.g. compiler experts who work with similar languages), and hold discussions on the specification as you work.&lt;/p&gt;&lt;p&gt;This is also a good time to start working on your second implementation. At this point, you will have a good grasp on the overall compiler design, the flaws from your original implementation, and better skills as a compiler programmer. Working on your second compiler and your specification at the same time can help as both endeavours inform the others — a particularly difficult detail to implement could lead to a simplification in the spec, and an under-specified detail getting shored up could lead to a more robust implementation.&lt;/p&gt;&lt;p&gt;Don’t get carried away — keep this new compiler simple and small. Don’t go crazy on nice-to-have features like linters and formatters, an exhaustive test suite, detailed error messages, a sophisticated optimizer, and so on. You want it to implement the specification as simply as possible, so that you can use it for the next step: the hosted compiler. You need to write a third implementation, using your own language to compile itself.&lt;/p&gt;&lt;p&gt;The second compiler, which I hope you wrote in C, is now the bootstrap compiler. I recommend keeping it up-to-date with the specification and maintaining it perpetually as a convenient path to bootstrap your toolchain from scratch (looking at you, Rust). But it’s not going to be the final implementation: any self-respecting general-purpose programming language is implemented in itself. The next, and final step, is to implement your language for a third time.&lt;/p&gt;&lt;p&gt;At this point, you will have refined and proven your language design. You will have developed and applied compiler programming skills. You will have a robust implementation for a complete and self-consistent programming language, developed carefully and with the benefit of hindsight. Your future community will thank you for the care and time you put into this work, as your language design and implementation sets the ceiling on the quality of programs written in it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-design-a-new-programming-language/</link>
        
        <pubDate>Fri, 25 Dec 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-design-a-new-programming-language/</guid>
      </item>
    
      <item>
        
        
          <title>godocs.io is now available</title>
          <description>
            &lt;p&gt;Due to the coming sunsetting of godoc.org in favor of pkg.go.dev, I’m happy to announce that &lt;a href=&quot;https://godocs.io&quot; target=&quot;_blank&quot;&gt;godocs.io&lt;/a&gt; is now available as a replacement. We have &lt;a href=&quot;https://sr.ht/~sircmpwn/godocs.io&quot; target=&quot;_blank&quot;&gt;forked the codebase&lt;/a&gt; and cleaned things up quite a bit, removing lots of dead or obsolete features, cleaning out a bunch of Google-specific code and analytics, reducing the JavaScript requirements, and rewriting the search index for Postgres. We will commit to its maintenance going forward for anyone who prefers the original godoc.org experience over the new website.&lt;/p&gt;&lt;p&gt;Notice: this article was rewritten on 2021-01-19. The original article has a lot of unnecessary salt. &lt;a href=&quot;https://git.sr.ht/~sircmpwn/drewdevault.com/tree/fa8799c882f6555606e0a906bd8e0a484bb51398/item/content/blog/godocs.io.md&quot; target=&quot;_blank&quot;&gt;You can read the original here&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/godocs.io/</link>
        
        <pubDate>Fri, 18 Dec 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/godocs.io/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, December 2020</title>
          <description>
            &lt;p&gt;Happy holidays! I hope everyone’s having a great time staying at home and not spending any time with your families. It’s time for another summary of the month’s advances in FOSS development. Let’s get to it!&lt;/p&gt;&lt;p&gt;One of my main focuses has been on sourcehut’s API 2.0 planning. This month, the meta.sr.ht and git.sr.ht GraphQL APIs have shipped feature parity with the REST APIs, and the RFC 6749 compatible OAuth 2.0 implementation has shipped. I’ve broken ground on the todo.sr.ht GraphQL API — it’ll be next. Check out the &lt;a href=&quot;https://man.sr.ht/graphql.md&quot; target=&quot;_blank&quot;&gt;GraphQL docs on man.sr.ht&lt;/a&gt; if you want to kick the tires.&lt;/p&gt;&lt;p&gt;I also wrote a little tool this month called &lt;a href=&quot;https://git.sr.ht/~sircmpwn/mkproof&quot; target=&quot;_blank&quot;&gt;mkproof&lt;/a&gt;, after brainstorming some ways to allow sourcehut signups over Tor without enabling abuse. The idea is that you can generate a challenge (mkchallenge), give it to a user who generates a proof for that challenge (mkproof), and then verify their proof is correct. Generating the proof is computationally expensive and resistant to highly parallel attacks (e.g. GPUs), and takes tens of minutes of work — making it unpractical for spammers to register accounts in bulk, while still allowing Tor users to register with their anonymity intact.&lt;/p&gt;&lt;p&gt;On the Gemini front, patches from Mark Dain, William Casarin, and Eyal Sawady have improved &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gmnisrv&quot; target=&quot;_blank&quot;&gt;gmnisrv&lt;/a&gt; in several respects — mainly bugfixes — and &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gmni&quot; target=&quot;_blank&quot;&gt;gmnlm&lt;/a&gt; has grown the “&lt;n&gt;|” command, which pipes the Nth link into a shell command. Thanks are due to Alexey Yerin as well, who sent a little bugfix with redirect handling.&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-devault-bare/&quot; target=&quot;_blank&quot;&gt;second draft of the BARE specification&lt;/a&gt; was submitted to the IETF this month. Will revisit it again in several weeks. John Mulligan has also sent several patches improving go-bare — thanks!&lt;/p&gt;&lt;p&gt;scdoc 1.11.0 was released this month, with only minor bug fixes.&lt;/p&gt;&lt;p&gt;The secret project has slowed down a bit as we’ve started on a new phase of development: writing the specification, and new compiler which implements it from the ground up. Progress on this is good, but won’t introduce anything groundbreaking for a while. Stay tuned.&lt;/p&gt;&lt;p&gt;That’s all for now! I’ll see you in a month.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-December-2020/</link>
        
        <pubDate>Tue, 15 Dec 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-December-2020/</guid>
      </item>
    
      <item>
        
        
          <title>Become shell literate</title>
          <description>
            &lt;p&gt;Shell literacy is one of the most important skills you ought to possess as a programmer. The Unix shell is one of the most powerful ideas ever put to code, and should be second nature to you as a programmer. No other tool is nearly as effective at commanding your computer to perform complex tasks quickly — or at storing them as scripts you can use later.&lt;/p&gt;&lt;p&gt;In my workflow, I use Vim as my editor, and Unix as my “IDE”. I don’t trick out &lt;a href=&quot;https://git.sr.ht/~sircmpwn/dotfiles/tree/master/.vimrc&quot; target=&quot;_blank&quot;&gt;my vimrc&lt;/a&gt; to add a bunch of IDE-like features — the most substantial plugin I use on a daily basis is &lt;a href=&quot;https://github.com/ctrlpvim/ctrlp.vim&quot; target=&quot;_blank&quot;&gt;Ctrl+P&lt;/a&gt;, and that just makes it easier to open files. Being Vim literate is a valuable skill, but an important detail is knowing when to drop it. My daily workflow involves several open terminals, generally one with Vim, another to run builds or daemons, and a third which just keeps a shell handy for anything I might ask of it.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://redacted.moe/f/1bbaf26c.png&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/1bbaf26c.png&quot;&gt;
&lt;figcaption&gt;Screenshot of my workspace&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The shell I keep open allows me to perform complex tasks and answer complex questions as I work. I find interesting things with &lt;a href=&quot;https://git-scm.com/docs/git-grep&quot; target=&quot;_blank&quot;&gt;git grep&lt;/a&gt;, perform bulk find-and-replace with &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html#top&quot; target=&quot;_blank&quot;&gt;sed&lt;/a&gt;, answer questions with &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/awk.html#top&quot; target=&quot;_blank&quot;&gt;awk&lt;/a&gt;, and perform more intricate tasks on-demand with ad-hoc shell commands and pipelines. I have the freedom to creatively solve problems without being constrained to the rails laid by IDE designers.&lt;/p&gt;&lt;p&gt;Here’s an example of a problem I encountered recently: I had a bunch of changes in a git repository. I wanted to restore deleted files without dropping the rest of my changes, but there were hundreds of these. How can I efficiently address this problem?&lt;/p&gt;&lt;p&gt;Well, I start by getting a grasp of the scale of the issue with git status, which shows hundreds of deleted files that need to be restored. This scale is beyond the practical limit of manual intervention, so I switch to git status -s to get a more pipeline-friendly output.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ git status -s
 D main/a52dec/APKBUILD
 D main/a52dec/a52dec-0.7.4-build.patch
 D main/a52dec/automake.patch
 D main/a52dec/fix-globals-test-x86-pie.patch
 D main/aaudit/APKBUILD
 D main/aaudit/aaudit
 D main/aaudit/aaudit-common.lua
 D main/aaudit/aaudit-repo
 D main/aaudit/aaudit-server.json
 D main/aaudit/aaudit-server.lua
 ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I can work with this. I add grep &apos;^ D&apos; to filter out any entries which were not deleted, and pipe it through awk &apos;{ print $2 }&apos; to extract just the filenames. I’ll often run the incomplete pipeline just to check my work and catch my bearings:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ git status -s | grep &amp;apos;^ D&amp;apos; | awk &amp;apos;{ print $2 }&amp;apos;
main/a52dec/APKBUILD
main/a52dec/a52dec-0.7.4-build.patch
main/a52dec/automake.patch
main/a52dec/fix-globals-test-x86-pie.patch
main/aaudit/APKBUILD
main/aaudit/aaudit
main/aaudit/aaudit-common.lua
main/aaudit/aaudit-repo
main/aaudit/aaudit-server.json
main/aaudit/aaudit-server.lua
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Very good — we have produced a list of files which we need to address. Note that, in retrospect, I could have dropped the grep and just used awk to the same effect:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ git status -s | awk &amp;apos;/^ D/ { print $2 }&amp;apos;
main/a52dec/APKBUILD
main/a52dec/a52dec-0.7.4-build.patch
main/a52dec/automake.patch
main/a52dec/fix-globals-test-x86-pie.patch
main/aaudit/APKBUILD
main/aaudit/aaudit
main/aaudit/aaudit-common.lua
main/aaudit/aaudit-repo
main/aaudit/aaudit-server.json
main/aaudit/aaudit-server.lua
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, we’re just writing an ad-hoc command here to solve a specific, temporary problem — finesse is not important. This command isn’t going to be subjected to a code review. Often my thinking in these situations is to solve one problem at a time: “filter the list” and “reword the list”.  Anyway, the last step is to actually use this list of files to address the issue, with the help of &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/xargs.html#top&quot; target=&quot;_blank&quot;&gt;xargs&lt;/a&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ git status -s | awk &amp;apos;/^ D/ { print $2 }&amp;apos; | xargs git checkout --
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let’s look at some more examples of interesting ad-hoc shell pipelines. Naturally, I wrote a shell pipeline to find some:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ history | cut -d&amp;apos; &amp;apos; -f2- | awk -F&amp;apos;|&amp;apos; &amp;apos;{ print NF-1 &amp;quot; &amp;quot; $0 }&amp;apos; | sort -n | tail
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here’s the breakdown:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;history&lt;/code&gt; prints a list of my historical shell commands.&lt;/li&gt;&lt;li&gt;&lt;code&gt;cut -d&amp;apos; &amp;apos; -f2-&lt;/code&gt; removes the first field from each line, using space as a delimiter. &lt;code&gt;history&lt;/code&gt; numbers every command, and this removes the number.&lt;/li&gt;&lt;li&gt;&lt;code&gt;awk -F&amp;apos;|&amp;apos; &amp;apos;{ print NF-1 &amp;quot; &amp;quot; $0 }&lt;/code&gt; tells awk to use | as the field delimiter for each line, and print each line prefixed with the number of fields. This prints every line of my history, prefixed with the number of times the pipe operator appears in that line.&lt;/li&gt;&lt;li&gt;&lt;code&gt;sort -n&lt;/code&gt; numerically sorts this list.&lt;/li&gt;&lt;li&gt;&lt;code&gt;tail&lt;/code&gt; prints the last 10 items.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This command, written in the moment, finds, characterizes, filters, and sorts my shell history by command complexity. Here are a couple of the cool shell commands I found:&lt;/p&gt;&lt;p&gt;Play the 50 newest videos in a directory with &lt;a href=&quot;https://github.com/mpv-player/mpv&quot; target=&quot;_blank&quot;&gt;mpv&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ls -tc | head -n50 | tr &amp;apos;\n&amp;apos; &amp;apos;\0&amp;apos; | xargs -0 mpv
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I use this command all the time. If I want to watch a video later, I will &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html#top&quot; target=&quot;_blank&quot;&gt;touch&lt;/a&gt; the file so it appears at the top of this list. Another command transmits a tarball of a patched version of &lt;a href=&quot;http://www.celestegame.com/&quot; target=&quot;_blank&quot;&gt;Celeste&lt;/a&gt; to a friend using netcat, minus the (large) game assets, with a progress display via &lt;a href=&quot;http://www.ivarch.com/programs/pv.shtml&quot; target=&quot;_blank&quot;&gt;pv&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;find . ! -path &amp;apos;./Content/*&amp;apos; | xargs tar -cv | pv | zstd | nc 204:fbf5:... 12345
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And on my friend’s end:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;nc -vll :: 12345 | zstdcat | pv | tar -xv
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;tar, by the way, is an under-rated tool for moving multiple files through a pipeline. It can read and write tarballs to stdin and stdout!&lt;/p&gt;&lt;p&gt;I hope that this has given you a tantalizing taste of the power of the Unix shell. If you want to learn more about the shell, I can recommend &lt;a href=&quot;http://shellhaters.org/&quot; target=&quot;_blank&quot;&gt;shellhaters.org&lt;/a&gt; as a great jumping-off point into various shell-related parts of the POSIX specification. Don’t be afraid of the spec — it’s concise, comprehensive, comprehensible, and full of examples. I would also &lt;em&gt;definitely&lt;/em&gt; recommend taking some time to learn awk in particular: &lt;a href=&quot;https://ferd.ca/awk-in-20-minutes.html&quot; target=&quot;_blank&quot;&gt;here’s a brief tutorial&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Shell-literacy/</link>
        
        <pubDate>Sat, 12 Dec 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Shell-literacy/</guid>
      </item>
    
      <item>
        
        
          <title>Web analytics should at least meet the standards of informed consent</title>
          <description>
            &lt;p&gt;Research conducted on human beings, at least outside of the domain of technology, has to meet a minimum standard of ethical reasoning called &lt;a href=&quot;https://en.wikipedia.org/wiki/Informed_consent&quot; target=&quot;_blank&quot;&gt;informed consent&lt;/a&gt;. Details vary, but the general elements of informed consent are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Disclosure of the nature and purpose of the research and its implications (risks and benefits) for the participant, and the confidentiality of the collected information.&lt;/li&gt;&lt;li&gt;An adequate understanding of these facts on the part of the participant, requiring an accessible explanation in lay terms and an assessment of understanding.&lt;/li&gt;&lt;li&gt;The participant must exercise voluntary agreement, without coercion or fear of repercussions (e.g. not being allowed to use your website).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;So, I pose the following question: if your analytics script wouldn’t pass muster at your university’s ethics board, then what the hell is it doing on your website? Can we not meet this basic minimum standard of ethical decency and respect for our users?&lt;/p&gt;&lt;p&gt;Opt-out is not informed consent. Manually unticking dozens of third-party trackers from a cookie pop-up is not informed consent. “By continuing to use this website, you agree to…” is not informed consent. “Install &lt;a href=&quot;https://ublockorigin.com/&quot; target=&quot;_blank&quot;&gt;uBlock Origin&lt;/a&gt;” is not informed consent.&lt;/p&gt;&lt;p&gt;I don’t necessarily believe that ethical user tracking is &lt;em&gt;impossible&lt;/em&gt;, but I know for damn sure that most of these “pro-privacy” analytics solutions which have been cropping up in the wake of the GDPR don’t qualify, either.&lt;/p&gt;&lt;p&gt;Our industry’s fundamental failure to respect users, deliberately mining their data without consent and without oversight for profit, is the reason why we’re seeing legal crackdowns in the form of the GDPR and similar legislation.  Our comeuppance is well-earned, and I hope that the regulators give it teeth in enforcement. The industry response — denial and looking for ways to weasel out of these ethical obligations — is a strategy on borrowed time. The law is not a computer program, and it is not executed by computers: it is executed by human beings who can see through your horseshit. You’re not going to be able to seek out some narrow path you can walk to skirt the regulations and keep spying on people.&lt;/p&gt;&lt;p&gt;You’re going to stop spying on people.&lt;/p&gt;&lt;p&gt;&lt;em&gt;P.S. If you still want the data you might get from analytics without compromising on ethics, here’s an idea: compensate users for their participation in your research. Woah, what a wild idea! That’s not very growth hacker of you, Drew.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Analytics-and-informed-consent/</link>
        
        <pubDate>Fri, 04 Dec 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Analytics-and-informed-consent/</guid>
      </item>
    
      <item>
        
        
          <title>A few ways to make money in FOSS</title>
          <description>
            &lt;p&gt;I work on free and open-source software full time, and I make a comfortable living doing it. And I don’t half-ass it: 100% of my code is free and open-source. There’s no proprietary add-ons, no periodic code dumps, just 100% bona-fide free and open source software. Others have often sought my advice — how can they, too, make a living doing open source?&lt;/p&gt;&lt;p&gt;Well, there’s more than one way to skin a cat. There are many varieties of software, each with different needs, and many kinds of people, each with different needs. The exact approach which works for you and your project will vary quite a bit depending on the nature of your project.&lt;/p&gt;&lt;p&gt;I would generally categorize my advice into two bins:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You want to make money from your own projects&lt;/li&gt;&lt;li&gt;You want to make money participating in open source&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The first one is more difficult. We’ll start with the latter.&lt;/p&gt;&lt;h2&gt;Being employed in FOSS&lt;/h2&gt;&lt;p&gt;One way to make money in FOSS is to get someone to pay you to write free software. There’s lots of advantages to this: minimal personal risk, at-market salaries, benefits, and so on, but at the cost of not necessarily getting to choose what you work on all the time.&lt;/p&gt;&lt;p&gt;I have a little trick that I often suggest to people who vaguely want to work “in FOSS”, but who aren’t trying to find the monetization potential in their own projects. Use git to clone the source repositories for some (large) projects you’re interested in, &lt;a href=&quot;https://drewdevault.com/2020/08/10/How-to-contribute-to-FOSS.html&quot; target=&quot;_blank&quot;&gt;the kind of stuff you want to work on&lt;/a&gt;, and then run this command:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;git log -n100000 --format=&amp;quot;%ae&amp;quot; | cut -d@ -f2 | sort | uniq -c | sort -nr | less
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will output a list of the email domains who have committed to the repository in the last 100,000 commits. This is a good set of leads for companies who might be interested in paying you to work on projects like this 😉&lt;/p&gt;&lt;p&gt;Another good way is to explicitly seek out large companies known to work a lot in FOSS, and see if they’re hiring in those departments. There are some companies that specialize in FOSS, such as RedHat, Collabora, and dozens more; and there are large companies with FOSS-specific teams, such as Intel, AMD, IBM, and so on.&lt;/p&gt;&lt;h2&gt;Making money from your own FOSS work&lt;/h2&gt;&lt;p&gt;If you want to pay for the project infrastructure, and maybe beer money for the weekend, then donations are an easy way to do that. I’ll give it to you straight, though: you’re unlikely to make a living from donations.  Programmers who do are a small minority. If you want to make a living from FOSS, it’s going to be more difficult.&lt;/p&gt;&lt;p&gt;Start by unlearning what you think you know about startups. The toxic startup culture around venture capital and endless hyper-growth is more stressful, less likely to succeed, and socially irresponsible. Building a sustainable business responsibly takes time, careful planning, and hard work. The fast route — venture capital funded — is going to impose constraints on your business that will ultimately make it difficult to remain true to your open-source mission.&lt;/p&gt;&lt;p&gt;And yes, you are building a &lt;em&gt;business&lt;/em&gt;. You need to start thinking of your project as a business and of yourself as a business owner. This undertaking is going to require developing business skills in planning, budgeting, scheduling, resource allocation, marketing &amp; sales, compliance, and more. At times, you will be forced to embrace your inner &lt;a href=&quot;http://www.catb.org/jargon/html/S/suit.html&quot; target=&quot;_blank&quot;&gt;suit&lt;/a&gt;. Channel your engineering problem-solving skills into the business problems.&lt;/p&gt;&lt;p&gt;So, you’ve got the right mindset. What are some business models that work?&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut&lt;/a&gt;, my company, has two revenue streams. We have a hosted SaaS product. It’s open source, and users can choose to deploy and maintain it themselves, or they can just buy a hosted account from us. The services are somewhat complex, so the managed offering saves them a lot of time. We have skilled sysops/sysadmins, support channels, and so on, for paying users. Importantly, we don’t have a free tier (but we do choose to provide free service to those who need it, at our discretion).&lt;/p&gt;&lt;p&gt;Our secondary revenue stream is &lt;a href=&quot;https://sourcehut.org/consultancy&quot; target=&quot;_blank&quot;&gt;free software consulting&lt;/a&gt;. Our developers work part-time writing free and open-source software on contracts. We’re asked to help implement features upstream for various projects, or to develop new open-source applications or libraries, to share our expertise in operations, and so on, and charge for these services. This is different from providing paid support or development on our own projects — we accept contracts to work on &lt;em&gt;any&lt;/em&gt; open source project.&lt;/p&gt;&lt;p&gt;The other approach to consulting is also possible: paid support and development on your own projects. If there are businesses that rely on your project, then you may be able to offer them support or develop new features or bugfixes that they need, on a paid basis. Projects with a large corporate userbase also sometimes &lt;em&gt;do&lt;/em&gt; find success in donations — albeit rebranded as sponsorships. The largest projects often set up foundations to manage them in this manner.&lt;/p&gt;&lt;p&gt;These are, in my experience, some of the most successful approaches to monetizing FOSS. You may have success with a combination of these, or with other business models as well.  Remember to turn that engineering mind of yours towards the task of monetization, and experiment with and invent new ways of making money that best suit the kind of software you want to work on.&lt;/p&gt;&lt;p&gt;Feel free to &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;reach out&lt;/a&gt; if you have some questions or need a sounding board for your ideas. Good luck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-few-ways-to-make-money-in-FOSS/</link>
        
        <pubDate>Fri, 20 Nov 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-few-ways-to-make-money-in-FOSS/</guid>
      </item>
    
      <item>
        
        
          <title>We can do better than DuckDuckGo</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://duckduckgo.com&quot; target=&quot;_blank&quot;&gt;DuckDuckGo&lt;/a&gt; is one of the long-time darlings of the technophile’s pro-privacy recommendations, and in fact the search engine that I use myself on the daily. They certainly present a more compelling option than many of the incumbents, like Google or Bing. Even so, DuckDuckGo is not good enough, and we ought to do better.&lt;/p&gt;&lt;p&gt;I have three grievances with DuckDuckGo:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;It’s not open source.&lt;/strong&gt; Almost all of DDG’s software is proprietary, and they’ve demonstrated &lt;a href=&quot;https://github.com/duckduckgo/Android/issues/527&quot; target=&quot;_blank&quot;&gt;gross incompetence&lt;/a&gt; in privacy in what little software they have made open source. Who knows what else is going on in the proprietary code?&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DuckDuckGo is not a search engine&lt;/strong&gt;. It’s more aptly described as a search engine frontend. They &lt;em&gt;do&lt;/em&gt; handle features like bangs and instant answers internally, but their actual search results come from third-parties like Bing. They don’t operate a crawler for their search results, and are not independent.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;The search results suck!&lt;/strong&gt; The authoritative sources for anything I want to find are almost always buried beneath 2-5 results from content scrapers and blogspam. This is also true of other search engines like Google. Search engines are highly vulnerable to abuse and they aren’t doing enough to address it.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;There are some FOSS attempts to do better here, but they all fall flat. &lt;a href=&quot;https://github.com/bauruine/searx/&quot; target=&quot;_blank&quot;&gt;searX&lt;/a&gt; is also a false search engine — that is, they serve someone else’s results. &lt;a href=&quot;https://yacy.net/&quot; target=&quot;_blank&quot;&gt;YaCy&lt;/a&gt; has their own crawler, but the distributed design makes results untolerably slow, poor quality, and vulnerable to abuse, and it’s missing strong central project leadership.&lt;/p&gt;&lt;p&gt;We need a real, working FOSS search engine, complete with its own crawler.&lt;/p&gt;&lt;p&gt;Here’s how I would design it.&lt;/p&gt;&lt;p&gt;First, YaCy-style decentralization is &lt;em&gt;way&lt;/em&gt; too hard to get right, especially when a search engine project already has a lot of Very Hard problems to solve. Federation is also very hard in this situation — queries will have to consult &lt;em&gt;most&lt;/em&gt; instances in order to get good quality results, or a novel sharding algorithm will have to be designed, and either approach will have to be tolerant of nodes appearing and disappearing at any time. Not to mention it’d be slow! Several unsolved problems with federation and decentralziation would have to be addressed on top of building a search engine in the first place.&lt;/p&gt;&lt;p&gt;So, a SourceHut-style approach is better. 100% of the software would be free software, and third parties would be encouraged to set up their own installations. It would use standard protocols and formats where applicable, and accept patches from the community. However, the database would still be centralized, and even if programmable access were provided, it would not be with an emphasis on decentralization or shared governance. It might be possible to design tools which help third-parties bootstrap their indexes, and create a community of informal index sharing, but that’s not the focus here.&lt;/p&gt;&lt;p&gt;It would also need its own crawler, and probably its own indexer. I’m not convinced that any of the existing FOSS solutions in this space are quite right for this problem. Crucially, I would &lt;em&gt;not&lt;/em&gt; have it crawling the entire web from the outset. Instead, it should crawl a whitelist of domains, or “tier 1” domains. These would be the limited mainly to authoritative or high-quality sources for their respective specializations, and would be weighed upwards in search results. Pages that these sites link to would be crawled as well, and given tier 2 status, recursively up to an arbitrary N tiers. Users who want to find, say, a blog post about a subject rather than the documentation on that subject, would have to be more specific: “$subject blog posts”.&lt;/p&gt;&lt;p&gt;An advantage of this design is that it would be easy for anyone to take the software stack and plop it on their own servers, with their own whitelist of tier 1 domains, to easily create a domain-specific search engine. Independent groups could create search engines which specialize in academia, open standards, specific fandoms, and so on. They could tweak their precise approach to indexing, tokenization, and so on to better suit their domain.&lt;/p&gt;&lt;p&gt;We should also prepare the software to boldly lead the way on new internet standards. Crawling and indexing non-HTTP data sources (Gemini?  Man pages? Linux distribution repositories?), supporting non-traditional network stacks (Tor? Yggdrasil? cjdns?) and third-party name systems (OpenNIC?), and anything else we could leverage our influence to give a leg up on.&lt;/p&gt;&lt;p&gt;There’s a &lt;em&gt;ton&lt;/em&gt; of potential in this domain which is just sitting on the floor right now. The main problem is: who’s going to pay for it? Advertisements or paid results are &lt;em&gt;not&lt;/em&gt; going to fly — conflict of interest. Private, paid access to search APIs or index internals is one opportunity, but it’s kind of shit and I think that preferring open data access and open APIs would be exceptionally valuable for the community.&lt;/p&gt;&lt;p&gt;If SourceHut eventually grows in revenue — at least 5-10× its &lt;a href=&quot;https://sourcehut.org/blog/2020-11-11-sourcehut-q3-2020-financial-report/&quot; target=&quot;_blank&quot;&gt;present revenue&lt;/a&gt; — I intend to sponsor this as a public benefit project, with no plans for generating revenue. I am not aware of any monetization approach for a search engine which squares with my ethics and doesn’t fundamentally undermine the mission. So, if no one else has figured it out by the time we have the resources to take it on, we’ll do it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Better-than-DuckDuckGo/</link>
        
        <pubDate>Tue, 17 Nov 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Better-than-DuckDuckGo/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, November 2020</title>
          <description>
            &lt;p&gt;Greetings, humanoids! Our fleshy vessels have aged by 2.678×10⁶ seconds, and you know what that means: time for another status update! Pour a cup of your favorite beverage stimulant and gather ’round for some news.&lt;/p&gt;&lt;p&gt;First off, today is the second anniversary of SourceHut’s alpha being opened to the public, and as such, I’ve prepared a special &lt;a href=&quot;https://sourcehut.org/blog/2020-11-15-sourcehut-2-year-alpha/&quot; target=&quot;_blank&quot;&gt;blog post&lt;/a&gt; for you to read. I’ll leave the sr.ht details out of this post and just send you off to read about it there.&lt;/p&gt;&lt;p&gt;What else is new? Well, a few things. For one, I’ve been working more on Gemini. I added CGI support to &lt;a href=&quot;https://sr.ht/~sircmpwn/gmnisrv&quot; target=&quot;_blank&quot;&gt;gmnisrv&lt;/a&gt; and wrote a few &lt;a href=&quot;https://git.sr.ht/~sircmpwn/cgi-scripts&quot; target=&quot;_blank&quot;&gt;CGI scripts&lt;/a&gt; to do neato Gemini things with. I’ve also added regexp routing and URL rewriting support. We can probably ship gmnisrv 1.0 as soon as the last few bugs are flushed out, and a couple of minor features are added, and we might switch to another SSL implementation as well. Thanks to the many contributors who’ve helped out: William Casarin, Tom Lebreux, Kenny Levinsen, Eyal Sawady, René Wagner, dbandstra, and mbays.&lt;/p&gt;&lt;p&gt;In &lt;a href=&quot;https://baremessages.org&quot; target=&quot;_blank&quot;&gt;BARE&lt;/a&gt; news: Elm, Erlang, Java, and Ruby implementations have appeared, and I have submitted a &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-devault-bare/&quot; target=&quot;_blank&quot;&gt;draft RFC&lt;/a&gt; to the IETF for standardization.&lt;/p&gt;&lt;p&gt;Finally, I wrote a new Wayland server for you. Its only dependencies are a POSIX system and a C11 compiler — and it works with Nvidia GPUs, or even systems without OpenGL support at all. Here’s the code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;poll.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;signal.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;sys/ioctl.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;sys/mman.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;sys/socket.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;sys/un.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;time.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;

	&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;int16_t&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;int32_t&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	  &lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt;{&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;,&lt;span class=&quot;property&quot;&gt;c&lt;/span&gt;,&lt;span class=&quot;property&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;x&lt;/span&gt;,&lt;span class=&quot;property&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;property&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;property&quot;&gt;h&lt;/span&gt;,&lt;span class=&quot;property&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;property&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	       &lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt;{&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;c&lt;/span&gt;)(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;u16&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

	  &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;pollfd&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;33&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;a&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0xFFFF&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
			      &lt;span class=&quot;type&quot;&gt;O&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;128&lt;/span&gt;][&lt;span class=&quot;number&quot;&gt;128&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;g&lt;/span&gt;[]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt; I z,I y,u32 x,u16 c
&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;SN&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;) (((n)%4)==0?(n):(n)+(4-((n)%4)))

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;u8w&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;ch&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;){&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0x3f&lt;/span&gt;)|&lt;span class=&quot;number&quot;&gt;0x80&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ch&lt;/span&gt;&amp;gt;&amp;gt;=&lt;span class=&quot;number&quot;&gt;6&lt;/span&gt;
&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ch&lt;/span&gt;|&lt;span class=&quot;number&quot;&gt;0xE0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;fwrite&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;xrgb&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;winsize&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ioctl&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;TIOCGWINSZ&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;ws_row&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;\x1b[H\x1b[2J\x1b[3J&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;ws_row&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;ws_col&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x2800&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[]&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;5&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;6&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;7&lt;/span&gt;}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;my&lt;/span&gt;)&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;mx&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mx&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mx&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;[((
&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;my&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;)/(&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;ws_row&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;/&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;((&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mx&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;)/(&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;ws_col&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;)]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;avg&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;((&lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0xFF&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;((&lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;
&amp;gt;&amp;gt;&lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0xFF&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;((&lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;&amp;gt;&amp;gt;&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0xFF&lt;/span&gt;))/&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;avg&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0x80&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;|=&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable function&quot;&gt;u8w&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;stdout&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable function&quot;&gt;putchar&lt;/span&gt;(
&lt;span class=&quot;number&quot;&gt;&amp;apos;\n&amp;apos;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable function&quot;&gt;fflush&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;stdout&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ao&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;)&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;){
&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;
&lt;span class=&quot;number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;)&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;)&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;i16&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;i16&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;
){&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;((&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;)&amp;lt;&amp;lt;&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;)|&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;ws&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;strlen&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;SN&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;rs&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;SN&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;a&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;ga&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,
&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;rs&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ao&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;:&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;
(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;6&lt;/span&gt;:&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;default&lt;/span&gt;:&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;
)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;gb&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ao&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;15&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;mmap&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;PROT_READ&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;MAP_PRIVATE&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;gc&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ao&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;gd&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;
:&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(!&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;xrgb&lt;/span&gt;
((&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;e&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;h&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;s&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;:&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;:&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;timespec&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;{&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;tv_sec&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;tv_nsec&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1.6e6&lt;/span&gt;}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;nanosleep&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ts&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;6&lt;/span&gt;:&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;12&lt;/span&gt;){&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;13&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;12&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;e&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;ge&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;){&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(
&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ao&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;12&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;gf&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;ae&lt;/span&gt;
&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;:&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ao&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;13&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;:&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ae&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;gg&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;:&lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;rs&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;)
&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sz&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;gh&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;:&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;
)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ao&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;go&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;e&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;h&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;
                  &lt;span class=&quot;property&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fmt&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}

&lt;span class=&quot;type&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;g&lt;/span&gt;[]&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;{{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;ga&lt;/span&gt;},{&lt;span class=&quot;string&quot;&gt;&amp;quot;wl_shm&amp;quot;&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;gb&lt;/span&gt;},{&lt;span class=&quot;string&quot;&gt;&amp;quot;wl_compositor&amp;quot;&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;gc&lt;/span&gt;},{&lt;span class=&quot;string&quot;&gt;&amp;quot;wl_subcompositor&amp;quot;&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;},
{&lt;span class=&quot;string&quot;&gt;&amp;quot;wl_data_device_manager&amp;quot;&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;},{&lt;span class=&quot;string&quot;&gt;&amp;quot;wl_output&amp;quot;&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;},{&lt;span class=&quot;string&quot;&gt;&amp;quot;wl_seat&amp;quot;&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;7&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;},{&lt;span class=&quot;string&quot;&gt;&amp;quot;xdg_wm_base&amp;quot;&lt;/span&gt;,
&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;ge&lt;/span&gt;},{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;gd&lt;/span&gt;},{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;},{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;},{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;},{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;gf&lt;/span&gt;},{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;gg&lt;/span&gt;},{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;},{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;gh&lt;/span&gt;}}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;gi&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AR&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;:&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;:&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ao&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;g&lt;/span&gt;)/&lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;g&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;])&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;g&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(!&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;)&lt;span class=&quot;keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;gl&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;strlen&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;wh&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;w&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;SN&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;gl&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;ws&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,
                &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}}&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;si&lt;/span&gt;(){&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}

&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;main&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_1&lt;/span&gt;,&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;_2&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;socket&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;AF_UNIX&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;SOCK_STREAM&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;sockaddr_un&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;{&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;sun_family&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;AF_UNIX&lt;/span&gt;}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;getenv&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;XDG_RUNTIME_DIR&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(!&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;/tmp&amp;quot;&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;{&lt;span class=&quot;constant variable function&quot;&gt;sprintf&lt;/span&gt;(
 &lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;sun_path&lt;/span&gt;,&lt;span class=&quot;string&quot;&gt;&amp;quot;%s/wayland-%d&amp;quot;&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;x&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt;(&lt;span class=&quot;constant variable function&quot;&gt;access&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;sun_path&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;F_OK&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;bind&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;, (
&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;sockaddr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;,&lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;listen&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;){&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;property&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;POLLIN&lt;/span&gt;|&lt;span class=&quot;constant variable&quot;&gt;POLLHUP&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;revents&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;signal&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;SIGINT&lt;/span&gt;,
&lt;span class=&quot;constant variable&quot;&gt;si&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;memset&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt;(&lt;span class=&quot;constant variable function&quot;&gt;poll&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;revents&lt;/span&gt;){&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;accept&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;z&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;revents&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;POLLHUP&lt;/span&gt;){
&lt;span class=&quot;constant variable function&quot;&gt;memmove&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;],&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;],&lt;span class=&quot;number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;memset&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;],&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}
&lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(!&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;revents&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;fds&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;[
&lt;span class=&quot;constant variable function&quot;&gt;CMSG_SPACE&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;)]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;cmsghdr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;iovec&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;{&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;iov_base&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;iov_len&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;msghdr&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;{&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;msg_iov&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;msg_iovlen&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;msg_control&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;msg_controllen&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;
(&lt;span class=&quot;constant variable&quot;&gt;q&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;recvmsg&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;m&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;CMSG_FIRSTHDR&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;m&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;){&lt;span class=&quot;constant variable&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable function&quot;&gt;CMSG_DATA&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;,&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;)
&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;((&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&amp;gt;&amp;gt;&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0xFFFF&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0xFFFF&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;){&lt;span class=&quot;constant variable function&quot;&gt;gi&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}&lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;{&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;){&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;g&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;c&lt;/span&gt;){&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;g&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;][&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;b&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;c&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;u&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}}&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)
                     {&lt;span class=&quot;constant variable function&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;t&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;a&lt;/span&gt;,&lt;span class=&quot;constant variable&quot;&gt;o&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}}}}&lt;span class=&quot;constant variable function&quot;&gt;unlink&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;sun_path&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’re welcome!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-November-2020/</link>
        
        <pubDate>Sun, 15 Nov 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-November-2020/</guid>
      </item>
    
      <item>
        
        
          <title>Utility vs usability</title>
          <description>
            &lt;p&gt;In many fields, professional-grade tooling requires a high degree of knowledge and training to use properly, usually more than is available to the amateur. The typical mechanic’s tool chest makes my (rather well-stocked, in my opinion) tool bag look quite silly. A racecar driver is using a vehicle which is much more complex than, say, the soccer mom’s mini-van. Professional-grade tools are, necessarily, more complex and require skill to use.&lt;/p&gt;&lt;p&gt;There are two attributes to consider when classifying these tools: &lt;em&gt;utility&lt;/em&gt; and &lt;em&gt;usability&lt;/em&gt;. These are not the same thing. Some tools have both high utility and high usability, such as a pencil. Some are highly usable, but of low utility, such as a child’s tricycle. Tools of both low-utility and low-usability are uncommon, but I’m sure you can think of a few examples from your own experiences :)&lt;/p&gt;&lt;p&gt;When designing tools, it is important to consider both of these attributes, and it helps to keep the intended audience in mind. I think that many programmers today are overly concerned with usability, and insufficiently concerned with utility. Some programmers (although this sort prefers “developer”) go so far as to fetishize usability &lt;em&gt;at the expense&lt;/em&gt; of utility.&lt;/p&gt;&lt;p&gt;In some cases, sacrificing utility in favor of usability is an acceptable trade-off. In the earlier example’s case, it’s unlikely that anyone would argue that the soccer mom should be loading the tots into an F1 racecar. However, it’s equally absurd to suppose that the F1 driver should bring a mini-van to the race track. In the realm of programming, this metaphor speaks most strongly to me in the design of programming tools.&lt;/p&gt;&lt;p&gt;I argue that most programmers are professionals who are going to invest several years into learning the craft. This is the audience for whom I design my tools. What trouble is it to spend an extra hour learning a somewhat less intuitive &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;code review tool&lt;/a&gt; when the programming language whose code you’re reviewing required months to learn and years to master?&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/1205/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/is_it_worth_the_time_2x.png&quot;&gt;
&lt;figcaption&gt;An XKCD comic which depicts a table that compares “time to improve task” versus “time saved by improvement” and how to figure if the improvement is worthwhile&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I write tools to maximize the productivity of professional programmers. Ideally, we can achieve both usability and utility, and often we do just that. But, sometimes, these tools require a steeper learning curve. If they are &lt;em&gt;more useful&lt;/em&gt; in spite of that, they will usually save heaps of time in the long run.&lt;/p&gt;&lt;p&gt;Instead of focusing on dumbing down our tools, maximizing usability at the expense of utility, we should focus on making powerful tools &lt;em&gt;and&lt;/em&gt; fostering a culture of &lt;em&gt;mentorship&lt;/em&gt;. Senior engineers should be helping their juniors learn and grow to embrace and build a new generation of more and more productive tooling, considering usability all the while but never at the expense of utility.&lt;/p&gt;&lt;p&gt;I’ll address mentorship in more detail in future posts. For now, I’ll just state that mentorship is the praxis of my tooling philosophy. We can build better, more powerful, and more productive tools, even if they require a steeper learning curve, so long as we’re prepared to teach people how to use them, and they’re prepared to learn.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Utility-vs-usability/</link>
        
        <pubDate>Fri, 06 Nov 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Utility-vs-usability/</guid>
      </item>
    
      <item>
        
        
          <title>What is this Gemini thing anyway, and why am I excited about it?</title>
          <description>
            &lt;p&gt;I’ve been writing about some specific topics in the realm of Gemini on my blog over the past two months or so, but I still haven’t written a broader introduction to Gemini, what I’m doing with it, and why you should be excited about it, too. Let’s do that today!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://gemini.circumlunar.space/&quot; target=&quot;_blank&quot;&gt;Gemini&lt;/a&gt; is a network protocol for exchanging hypertext documents — “hypertext” in the general sense of the word, not with respect to the hypertext markup language (HTML) that web browsers understand. It’s a simple network protocol which allows clients to request hypertext documents (in its own document format, gemtext). It is, in some respects, an evolution of &lt;a href=&quot;https://en.wikipedia.org/wiki/Gopher_(protocol)&quot; target=&quot;_blank&quot;&gt;Gopher&lt;/a&gt;, but more modernized and streamlined.&lt;/p&gt;&lt;p&gt;Gemini is very simple. The protocol uses TLS to establish an encrypted connection (using self-signed certificates and &lt;abbr title=&quot;trust on first use&quot;&gt;TOFU&lt;/abbr&gt; rather than certificate authorities), and performs a very simple exchange: the client sends the URL it wants to retrieve, terminated with CRLF.  The server responds with an informative line, consisting of a numeric status code and some additional information (such as the document’s mimetype), then writes the document and closes the connection. Authentication, if desired, is done with client certificates. User input, if desired, is done with a response code which conveys a prompt string and a request for user input, followed by a second request with the user’s response filled into the URL’s query string. And that’s pretty much it!&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ openssl s_client -quiet -crlf   \
    -servername drewdevault.com   \
    -connect drewdevault.com:1965 \
  | awk &amp;apos;{ print &amp;quot;response: &amp;quot; $0 }&amp;apos;
gemini://drewdevault.com
response: 20 text/gemini
response: ```ASCII art of a rocket next to &amp;quot;Drew DeVault&amp;quot; in a stylized font
response:   /\
response:   ||    ________                         ________       ____   ____            .__   __
response:   ||    \______ \_______   ______  _  __ \______ \   ___\   \ /   /____   __ __|  |_/  |_
response:  /||\    |    |  \_  __ \_/ __ \ \/ \/ /  |    |  \_/ __ \   Y   /\__  \ |  |  \  |\   __\
response: /:||:\   |    `   \  | \/\  ___/\     /   |    `   \  ___/\     /  / __ \|  |  /  |_|  |
response: |:||:|  /_______  /__|    \___  &amp;gt;\/\_/   /_______  /\___  &amp;gt;\___/  (____  /____/|____/__|
response: |/||\|        \/            \/                 \/     \/             \/
response:   **
response:   **
response: ```
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So why am I excited about it?&lt;/p&gt;&lt;p&gt;My disdain for web browsers is well documented&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Web browsers are &lt;em&gt;extraordinarily&lt;/em&gt; complex, and any attempt to build a new one would be a Sisyphean task. Successfully completing that implementation, if even possible, would necessarily produce a Lovecraftian mess: unmaintainable, full of security vulnerabilities, with gigabytes in RAM use and hours in compile times. And given that all of the contemporary web browsers that implement a sufficiently useful subset of web standards are ass and getting assier, what should we do?&lt;/p&gt;&lt;p&gt;The problem is unsolvable. We cannot have the “web” without all of these problems. But what we can have is something different, like Gemini. Gemini does not solve all of the web’s problems, but it addresses a subset of its use-cases better than the web does, and that excites me. I want to discard the parts of the web that Gemini does better, and explore other solutions for anything that’s left of the web which is worth keeping (hint: much of it is not).&lt;/p&gt;&lt;p&gt;There are some aspects of Gemini which I approve of immensely:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;It’s dead simple. A client or server implementation can be written from scratch by a single person in the space of an afternoon or two. A new web browser could take hundreds of engineers millions of hours to complete.&lt;/li&gt;&lt;li&gt;It’s not extensible. Gemini is designed to be &lt;em&gt;difficult&lt;/em&gt; to extend without breaking backwards compatibility, and almost all proposals for expansion on the mailing list are ultimately shot down. This is a good thing: extensibility is generally a bad idea. Extensions ultimately lead to more complexity and Gemini might suffer the same fate as the web if not for its disdain for extensions.&lt;/li&gt;&lt;li&gt;It’s opinionated about document formatting. There are no inline links (every link goes on its own line), no formatting, and no inline images. Gemini strictly separates the responsibility of content and presentation. Providing the content is the exclusive role of the server, and providing the presentation is the exclusive role of the client. There are no stylesheets and authors have very little say in &lt;em&gt;how&lt;/em&gt; their content is presented. It’s still possible for authors to express themselves within these constraints — as with any other constraints — but it allows clients to be simpler and act more as &lt;em&gt;user&lt;/em&gt; agents than &lt;em&gt;vendor&lt;/em&gt; agents.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Some people argue that what we should have is “the web, but less of it”, i.e. a “sane” subset of web standards. I don’t agree (for one, I don’t think there &lt;em&gt;is&lt;/em&gt; a “sane” subset of those standards), but I’ll save that for another blog post. Gemini is a new medium, and it’s different from the web. Anyone who checking it out should be prepared for that and open to working within its constraints. Limitations breed creativity!&lt;/p&gt;&lt;p&gt;For my part, I have been working on a number of Gemini projects. For one, this blog is now available &lt;a href=&quot;gemini://drewdevault.com&quot; target=&quot;_blank&quot;&gt;on Gemini&lt;/a&gt;, and I have started writing some Gemini-exclusive content for it. I’ve also written some software you’re welcome to use:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/gmni&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;libgmni&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://sr.ht/~sircmpwn/gmni&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;gmni&lt;/strong&gt;&lt;/a&gt;, and &lt;a href=&quot;https://sr.ht/~sircmpwn/gmni&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;gmnlm&lt;/strong&gt;&lt;/a&gt; are my suite of Gemini client software, all written in C11 and only depending on a POSIX-like system and OpenSSL. libgmni is a general-purpose Gemini client library with &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gmni/tree/master/include/gmni&quot; target=&quot;_blank&quot;&gt;a simple interface&lt;/a&gt;. gmni is a cURL-like &lt;a href=&quot;https://drewdevault.com/gmni.1.html&quot; target=&quot;_blank&quot;&gt;command line tool&lt;/a&gt; for performing Gemini requests. Finally, gmnlm is a line-mode browser with a rich feature-set. Together these tools weigh just under 4,000 lines of code, of which about 1,600 are the URL parser from cURL vendored in.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://portal.drewdevault.com/gmnisrv.gmi&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;gmnisrv&lt;/strong&gt;&lt;/a&gt; is a high-performance Gemini server, also written in C11 for POSIX systems with OpenSSL. It supports zero-configuration TLS, CGI scripting, auto-indexing, regex routing and URL rewrites, and I have a couple more things planned for 1.0. It clocks in at about 6,700 lines, of which the same 1,600 are vendored from cURL, and an additional 2,800 lines are vendored from Fabrice Bellard’s &lt;a href=&quot;https://bellard.org/quickjs/&quot; target=&quot;_blank&quot;&gt;quickjs&lt;/a&gt; regex implementation.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://portal.drewdevault.com/kineto.gmi&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;kineto&lt;/strong&gt;&lt;/a&gt; is an HTTP-to-Gemini gateway, implemented as a single Go file (under 500 lines) with the assistance of ~adnano’s &lt;a href=&quot;https://sr.ht/~adnano/go-gemini/&quot; target=&quot;_blank&quot;&gt;go-gemini&lt;/a&gt; library. My Gemini blog &lt;a href=&quot;https://portal.drewdevault.com&quot; target=&quot;_blank&quot;&gt;is available through this portal&lt;/a&gt; if you would like to browse it.&lt;/p&gt;&lt;p&gt;So dive in and explore! Install gmnisrv on your server and set up a Gemini space for yourself. Read the feeds from &lt;a href=&quot;gemini://gemini.circumlunar.space/capcom/&quot; target=&quot;_blank&quot;&gt;CAPCOM&lt;/a&gt;. Write some software of your own!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/What-is-Gemini-anyway/</link>
        
        <pubDate>Sun, 01 Nov 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/What-is-Gemini-anyway/</guid>
      </item>
    
      <item>
        
        
          <title>I&apos;m handing over maintenance of wlroots and sway to Simon Ser</title>
          <description>
            &lt;p&gt;Over the past several months, I’ve been gradually weaning down my role in both projects, and as a contributor to Wayland in general. I feel that I’ve already accomplished everything I set out to do with Wayland — and more! I have been happily using sway as my daily driver for well over a year with no complaints or conspicuously absent features. For me, there’s little reason to stay involved. This will likely come as no surprise to many who’ve kept their ear to the ground in these communities.&lt;/p&gt;&lt;p&gt;Simon has been an important co-maintainer on wlroots and sway for several years, and also serves as a maintainer for Wayland itself, and Weston. I trust him with these projects, and he’s been doing a stellar job so far — no real change in his work is necessary for this hand-off. Simon works for SourceHut full-time and his compensation covers his role in the Wayland community, so you can trust that the health of the project is unaffected, too.&lt;/p&gt;&lt;p&gt;There’s still plenty of great things to come from these projects without me. Many improvements are underway and more are planned for the future. Don’t worry: sway and wlroots have already demonstrated that they work quite well without my active involvement.&lt;/p&gt;&lt;p&gt;Good luck, Simon, and thanks for all of your hard work! I’m proud of you!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Im-handing-wlroots-and-sway-to-Simon/</link>
        
        <pubDate>Fri, 23 Oct 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Im-handing-wlroots-and-sway-to-Simon/</guid>
      </item>
    
      <item>
        
        
          <title>Firefox: The Jewel^WEmbarassment of Open Source</title>
          <description>
            &lt;p&gt;Circa 2006, the consensus on Firefox was concisely stated by this classic xkcd:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/198/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/perspective.png&quot;&gt;
&lt;figcaption&gt;A stick-figure comic. The title reads “Sometimes, when I first wake up, I am caught in the horrible grip of perspective.” The character, waking up, says “It may be a jewel of open source, but Firefox is just a browser. It shows webpages. What the hell is wrong with us?” The caption reads “Fortunately, this subsides quickly.”&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;This feeling didn’t last. In 2016, I wrote &lt;a href=&quot;https://drewdevault.com/blog/In-Memoriam-Mozilla/&quot;&gt;In Memoriam - Mozilla&lt;/a&gt;, and in 2017, &lt;a href=&quot;https://drewdevault.com/blog/Firefox-is-on-a-slippery-slope/&quot;&gt;Firefox is on a slippery slope&lt;/a&gt;. Well, I was right, and Firefox (and Mozilla) have only become worse since. The fuck-up culture is so ingrained in Mozilla in 2020 that it’s hard to see it ever getting better again.&lt;/p&gt;&lt;p&gt;In the time since my last article on the subject, Mozilla has:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Laid off 25% of its employees, mostly engineers, many of whom work on Firefox&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Raised executive pay 400% as their market share declined 85%&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Sent a record of all browsing traffic to CloudFlare by default&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Added advertisements to the new tab page on Firefox&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Used their brand to enter the saturated VPN grift market&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Built a walled garden for add-ons, then let the walls crash in&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Started, and killed, a dozen projects which were not Firefox&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fn-7-ref-1&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The most interesting things they’ve been involved in in the past few years are Rust and Servo, and they fired most or all of their engineers involved in both. And, yesterday, &lt;a href=&quot;https://blog.mozilla.org/blog/2020/10/20/mozilla-reaction-to-u-s-v-google/&quot; target=&quot;_blank&quot;&gt;Mozilla published a statement&lt;/a&gt; siding with Google on anti-trust, failing to disclose the fact that Google pays to keep their lights on.&lt;/p&gt;&lt;p&gt;Is this the jewel of open source? No, not anymore. Firefox is the embarrassment of open source, and it’s the only thing standing between Google and an all-encompassing monopoly over the web. Mozilla has divested from Firefox and started funnelling what money is left out of their engineering payroll and into their executive pockets. The web is dead, and its fetid corpse persists only as the layer of goop that Google scrapes between its servers and your screen. Anyone who still believes that Mozilla will save the web is a fool.&lt;/p&gt;&lt;p&gt;As I have &lt;a href=&quot;https://drewdevault.com/blog/Reckless-limitless-scope/&quot;&gt;stated before&lt;/a&gt;, the scope of web browsers has been increasing at a reckless pace for &lt;em&gt;years&lt;/em&gt;, to the point where it’s literally impossible to build a new web browser. We have no recourse left to preserve the web. This is why I’m throwing my weight behind &lt;a href=&quot;https://gemini.circumlunar.space/&quot; target=&quot;_blank&quot;&gt;Gemini&lt;/a&gt;, a new protocol which is &lt;em&gt;much simpler&lt;/em&gt; than the web, and which you can implement yourself in a weekend.&lt;/p&gt;&lt;p&gt;Forget about the web, it’s a lost cause. Let’s move on.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Firefox-the-embarassment-of-FOSS/</link>
        
        <pubDate>Thu, 22 Oct 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Firefox-the-embarassment-of-FOSS/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, October 2020</title>
          <description>
            &lt;p&gt;I’m writing this month’s status update from a brand-new desktop workstation (well, I re-used the GPU), my first new workstation in about 10 years. I hope this new one lasts for another decade! I aimed for something smaller and lightweight this time — it’s a Mini-ITX build. I’ve only been running this for a few days, so let me tell you about the last few accomplishments which are accountable to my venerable workstation’s final days of life.&lt;/p&gt;&lt;p&gt;First, there’s been a ton of important work completed for SourceHut’s API 2.0 plans. All of the main blockers for the first version of meta.sr.ht’s writable GraphQL API are resolved, and after implementing a few more resolvers it should be in a shippable state. This included riggings for database transactions, simplification of the mini-“ORM” I built, and support for asyncronous work like delivering webhooks. The latter called for a new library, &lt;a href=&quot;https://sr.ht/~sircmpwn/dowork/&quot; target=&quot;_blank&quot;&gt;dowork&lt;/a&gt;, which you’re free to reuse to bring asyncronous work processing to your Go programs.&lt;/p&gt;&lt;p&gt;I also built a new general-purpose daemon for SourceHut called &lt;a href=&quot;https://sr.ht/~sircmpwn/chartsrv/&quot; target=&quot;_blank&quot;&gt;chartsrv&lt;/a&gt;, which can be used to generate graphs from &lt;a href=&quot;https://prometheus.io/&quot; target=&quot;_blank&quot;&gt;Prometheus&lt;/a&gt; data. The following is a real-time graph of the load average on the builds.sr.ht workers:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://metrics.sr.ht/chart.svg?title=Build%20worker%20load%20average&amp;query=avg_over_time%28node_load15%7Binstance%3D~%22cirno%5B0-9%5D%2B.sr.ht%3A80%22%7D%5B1h%5D%29&amp;max=64&amp;since=336h&amp;stacked&amp;step=10000&amp;height=3&amp;width=10&quot;&gt;
&lt;figcaption&gt;A chart which hopefully shows a reasonable load average across all workers&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I’ve been getting more into &lt;a href=&quot;https://gemini.circumlunar.space/&quot; target=&quot;_blank&quot;&gt;Gemini&lt;/a&gt; this month, and have completed three (or four?) whole projects for it:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/gmni/&quot; target=&quot;_blank&quot;&gt;gmni&lt;/a&gt; and gmnlm: a client implementation and line-mode browser&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/gmnisrv/&quot; target=&quot;_blank&quot;&gt;gmnisrv&lt;/a&gt;: a server implementation&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://sr.ht/~sircmpwn/kineto/&quot; target=&quot;_blank&quot;&gt;kineto&lt;/a&gt;: an HTTP-&gt;Gemini portal&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The (arguably) fourth project is the completion of a Gemini version of this blog, which is available at &lt;code&gt;gemini://drewdevault.com&lt;/code&gt;, or via the kineto portal at &lt;a href=&quot;https://portal.drewdevault.com&quot; target=&quot;_blank&quot;&gt;portal.drewdevault.com&lt;/a&gt;. I’ll be posting some content exclusively on Gemini (and I already have!), so get yourself a client if you want to tune in.&lt;/p&gt;&lt;p&gt;I have also invested some effort into &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;himitsu&lt;/a&gt;, a project I shelved for so long that you probably don’t remember it. Worry not, I have rewritten the README.md to give you a better introduction to it. Here’s a screenshot for your viewing pleasure:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/13c10c49.png&quot;&gt;
&lt;figcaption&gt;A GUI dialog asking a user to consent to allow an application to access their IMAP credentials&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Bonus update: two new &lt;a href=&quot;https://baremessages.org&quot; target=&quot;_blank&quot;&gt;BARE&lt;/a&gt; implementations have appeared: OCaml and Java.&lt;/p&gt;&lt;p&gt;That’s all for now! I’ll see you for the next update soon. Thanks for your support!&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;...&lt;/summary&gt;
  &lt;img src=&quot;https://redacted.moe/f/26c6ba23.png&quot; alt=&quot;A screenshot of a page of a programming language specification detailing the syntax of tagged unions&quot;&gt;
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-October-2020/</link>
        
        <pubDate>Thu, 15 Oct 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-October-2020/</guid>
      </item>
    
      <item>
        
        
          <title>Four principles of software engineering</title>
          <description>
            &lt;p&gt;Software should be &lt;strong&gt;robust&lt;/strong&gt;. It should be designed to accommodate all known edge cases. In practice, this means predicting and handling all known error cases, enumerating and addressing all classes of user inputs, reasoning about and planning for the performance characteristics of your program, and so on.&lt;/p&gt;&lt;p&gt;Software should be &lt;strong&gt;reliable&lt;/strong&gt;. It should be expected to work for an extended length of time under design conditions without failures. Ideally, it should work outside of design conditions up to some threshold.&lt;/p&gt;&lt;p&gt;Software should also be &lt;strong&gt;stable&lt;/strong&gt;. It should not change in incompatible or unexpected ways; if it works today it should also work tomorrow. If it has to change, a plan shall be written. Stakeholders (including users!) should be given advance notice and should be involved in the planning stage.&lt;/p&gt;&lt;p&gt;Finally, software should be &lt;strong&gt;simple&lt;/strong&gt;. Only as many moving parts should be included as necessary to meet the other three goals. All software has bugs, but complicated software (1) has more bugs and (2) is more difficult to diagnose and fix. Note that designing a simple solution is usually more difficult than designing a complex solution.&lt;/p&gt;&lt;p&gt;&lt;em&gt;This (short) article is based on &lt;a href=&quot;https://cmpwn.com/@sir/104931806273081351&quot; target=&quot;_blank&quot;&gt;a Mastodon post&lt;/a&gt; I wrote a few weeks ago.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Four-principles-of-software-engineering/</link>
        
        <pubDate>Fri, 09 Oct 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Four-principles-of-software-engineering/</guid>
      </item>
    
      <item>
        
        
          <title>Spamtoberfest</title>
          <description>
            &lt;p&gt;As I’ve &lt;a href=&quot;https://drewdevault.com/2020/08/10/How-to-contribute-to-FOSS.html&quot; target=&quot;_blank&quot;&gt;written before&lt;/a&gt;, the best contributors to a FOSS project are intrinsically motivated to solve problems in your software. This sort of contribution is often fixing an important problem and places a smaller burden on maintainers to spend their time working with the contributor. I’ve previously contrasted this with the “I want to help out!” contributions, where a person just has a vague desire to help out. Those contributions are, generally, less valuable and place a greater burden on the maintainer. Now, DigitalOcean has lowered the bar even further with Hacktoberfest.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Disclaimer: I am the founder of a FOSS project hosting company similar to GitHub.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;As I write this, a Digital Ocean-sponsored and GitHub-enabled Distributed Denial of Service (DDoS) attack is ongoing, wasting the time of thousands of free software maintainers with an onslaught of meaningless spam. Bots are spamming &lt;a href=&quot;https://github.com/search?q=amazing+project+is:pr&amp;type=Issues&quot; target=&quot;_blank&quot;&gt;tens of thousands&lt;/a&gt; of pull requests like this:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/hundredrabbits/100r.co/pull/39/files&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/fd88f606.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a spam pull request on GitHub which adds garbage to the README.md file&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The official response from both Digital Ocean and GitHub appears to be passing the buck.  Digital Ocean addresses spam in their FAQ, putting the burden of dealing with it entirely on the maintainers:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Spammy pull requests can be given a label that contains the word “invalid” or “spam” to discount them. Maintainers are faced with the majority of spam that occurs during Hacktoberfest, and we dislike spam just as much as you. If you’re a maintainer, please label any spammy pull requests submitted to the repositories you maintain as “invalid” or “spam”, and close them. Pull requests with this label won’t count toward Hacktoberfest.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;via &lt;a href=&quot;https://hacktoberfest.digitalocean.com/details&quot; target=&quot;_blank&quot;&gt;Hacktoberfest FAQ&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Here’s GitHub’s response:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The content and activity you are reporting appears to be related to Hacktoberfest. Please keep in mind that GitHub Staff is not enforcing Hacktoberfest rules; we will, however, enforce our own Acceptable Use Policies. According to the Hacktoberfest FAQ… [same quote as given above]&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;via &lt;a href=&quot;https://twitter.com/kyleknighted/status/1311685461828612097&quot; target=&quot;_blank&quot;&gt;@kyleknighted@twitter.com&lt;/a&gt;&lt;/p&gt;&lt;p&gt;So, according to these two companies, whose responsibility is it to deal with the spam that &lt;em&gt;they’ve&lt;/em&gt; created? The maintainers, of course! All for a T-Shirt.&lt;/p&gt;&lt;p&gt;Let’s be honest. Hacktoberfest has never generated anything of value for open source. It’s a marketing stunt which sends a deluge of low-effort contributions to maintainers, leaving them to clean up the spam. I’ve never been impressed with Hacktoberfest contributions, even the ones which aren’t obviously written by a bot:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/whatwg/html/pull/5975/files&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/970f2a31.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a pull request which needlessly comment a CSS file&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Hacktoberfest is, and has always been, about one thing: marketing for Digital Ocean.&lt;/p&gt;&lt;iframe
src=&quot;https://oc.todon.fr/@val/104960502585461740/embed&quot;
class=&quot;mastodon-embed&quot;
style=&quot;max-width: 100%; border: 0; margin: 0 auto; display: block;&quot;
width=&quot;400&quot;
height=&quot;530&quot;
allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;This is what we get with corporate-sponsored “social coding”, brought to you by Digital Ocean and GitHub and McDonalds, home of the Big Mac™. When you build the Facebook of coding, you get the Facebook of coding. We don’t need to give away T-Shirts to incentivize drive-by drivel from randoms who will never get any closer to open source than a +1/-1 README.md change.&lt;/p&gt;&lt;p&gt;What would &lt;em&gt;actually&lt;/em&gt; benefit FOSS is to enable the strong mentorship necessary raise a new generation of &lt;strong&gt;software engineers&lt;/strong&gt; under the tutelage of maintainers who can rely on a strong support system to do their work. Programs like Google Summer of Code do this better. Programs where a marketing department spends $5,000 on T-Shirts to flood maintainers with garbage and clothe people in ads are doing the opposite: &lt;em&gt;hurting&lt;/em&gt; open source.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://redacted.moe/f/a50f2dfc.png&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/a50f2dfc.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a friend’s notifications, 9 out of 11 of which are spam&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Check out &lt;a href=&quot;https://twitter.com/shitoberfest&quot; target=&quot;_blank&quot;&gt;@shitoberfest on Twitter&lt;/a&gt; for more Hacktoberfest garbage.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update 2020-10-03&lt;/strong&gt;: Digital Ocean &lt;a href=&quot;https://hacktoberfest.digitalocean.com/hacktoberfest-update&quot; target=&quot;_blank&quot;&gt;has updated their rules&lt;/a&gt;, among other things asking maintainers to opt-in, to reduce spam.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Spamtoberfest/</link>
        
        <pubDate>Thu, 01 Oct 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Spamtoberfest/</guid>
      </item>
    
      <item>
        
        
          <title>A tale of two libcs</title>
          <description>
            &lt;p&gt;I received a bug report from Debian today, who had fed some garbage into &lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc&quot; target=&quot;_blank&quot;&gt;scdoc&lt;/a&gt;, and it gave them a SIGSEGV back. Diving into this problem gave me a good opportunity to draw a comparison between musl libc and glibc. Let’s start with the stack trace:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;==26267==ERROR: AddressSanitizer: SEGV on unknown address 0x7f9925764184
(pc 0x0000004c5d4d bp 0x000000000002 sp 0x7ffe7f8574d0 T0)
==26267==The signal is caused by a READ memory access.
    0 0x4c5d4d in parse_text /scdoc/src/main.c:223:61
    1 0x4c476c in parse_document /scdoc/src/main.c
    2 0x4c3544 in main /scdoc/src/main.c:763:2
    3 0x7f99252ab0b2 in __libc_start_main
/build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16
    4 0x41b3fd in _start (/scdoc/scdoc+0x41b3fd)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And if we pull up that line of code, we find…&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (!&lt;span class=&quot;constant variable function&quot;&gt;isalnum&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;last&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; ((&lt;span class=&quot;constant variable&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;FORMAT_UNDERLINE&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; !&lt;span class=&quot;constant variable function&quot;&gt;isalnum&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;next&lt;/span&gt;))) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hint: p is a valid pointer. “last” and “next” are both uint32_t. The segfault happens in the second call to isalnum. And, the key: it can only be reproduced on glibc, not on musl libc. If you did a double-take, you’re not alone. There’s nothing here which could have caused a segfault.&lt;/p&gt;&lt;p&gt;Since it was narrowed down to glibc, I pulled up the source code and went digging for the isalnum implementation, expecting some stupid bullshit. But before I get into their stupid bullshit, of which I can assure you there is &lt;em&gt;a lot&lt;/em&gt;, let’s briefly review the happy version. This is what the musl libc &lt;code&gt;isalnum&lt;/code&gt; implementation looks like:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isalnum&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isalpha&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isdigit&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isalpha&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; ((&lt;span class=&quot;type&quot;&gt;unsigned&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;|&lt;span class=&quot;number&quot;&gt;32&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;&amp;apos;a&amp;apos;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isdigit&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;unsigned&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;&amp;apos;0&amp;apos;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As expected, for any value of &lt;code&gt;c&lt;/code&gt;, isalnum will never segfault. Because why the fuck would isalnum segfault? Okay, now, let’s compare this to the &lt;a href=&quot;https://sourceware.org/git/?p=glibc.git;a=blob;f=ctype/ctype.h;h=351495aa4feaf23993fe65afc0760615268d044e;hb=HEAD&quot; target=&quot;_blank&quot;&gt;glibc implementation&lt;/a&gt;. When opening this header, you’re greeted with the typical GNU bullshit, but let’s trudge through and grep for isalnum.&lt;/p&gt;&lt;p&gt;The first result is this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;enum&lt;/span&gt;
{
  &lt;span class=&quot;constant variable&quot;&gt;_ISupper&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;_ISbit&lt;/span&gt; (&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;),        &lt;span class=&quot;comment&quot;&gt;/* UPPERCASE.  */&lt;/span&gt;
  &lt;span class=&quot;constant variable&quot;&gt;_ISlower&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;_ISbit&lt;/span&gt; (&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;),        &lt;span class=&quot;comment&quot;&gt;/* lowercase.  */&lt;/span&gt;
  &lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;constant variable&quot;&gt;_ISalnum&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;_ISbit&lt;/span&gt; (&lt;span class=&quot;number&quot;&gt;11&lt;/span&gt;)        &lt;span class=&quot;comment&quot;&gt;/* Alphanumeric.  */&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks like an implementation detail, let’s move on.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;constant variable function&quot;&gt;__exctype&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;isalnum&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But what’s &lt;code&gt;__exctype&lt;/code&gt;? Back up the file a few lines…&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;__exctype&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;) extern int name (int) __THROW
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, apparently that’s just the prototype. Not sure why they felt the need to write a macro for that. Next search result…&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#if&lt;/span&gt; !defined &lt;span class=&quot;constant variable&quot;&gt;__NO_CTYPE&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;# ifdef&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__isctype_f&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;__isctype_f&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;alnum&lt;/span&gt;)
&lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, this looks useful. What is &lt;code&gt;__isctype_f&lt;/code&gt;? Back up the file now…&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#ifndef&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__cplusplus&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;# define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;__isctype&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;type&lt;/span&gt;) \
  ((*__ctype_b_loc ())[(int) (c)] &amp;amp; (unsigned short int) type)
&lt;span class=&quot;keyword&quot;&gt;#elif&lt;/span&gt; defined &lt;span class=&quot;constant variable&quot;&gt;__USE_EXTERN_INLINES&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;# define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;__isctype_f&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;type&lt;/span&gt;) \
  __extern_inline int                                                         \
  is##type (int __c) __THROW                                                  \
  {                                                                           \
    return (*__ctype_b_loc ())[(int) (__c)] &amp;amp; (unsigned short int) _IS##type; \
  }
&lt;span class=&quot;keyword&quot;&gt;#endif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh…. oh dear. It’s okay, we’ll work through this together. Let’s see, &lt;code&gt;__isctype_f&lt;/code&gt; is some kind of inline function… wait, this is the else branch of &lt;code&gt;#ifndef __cplusplus&lt;/code&gt;. Dead end. Where the fuck is isalnum &lt;em&gt;actually&lt;/em&gt; defined? Grep again… okay… here we are?&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#if&lt;/span&gt; !defined &lt;span class=&quot;constant variable&quot;&gt;__NO_CTYPE&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;# ifdef&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__isctype_f&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;__isctype_f&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;alnum&lt;/span&gt;)
&lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;# elif&lt;/span&gt; defined &lt;span class=&quot;constant variable&quot;&gt;__isctype&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;# define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;isalnum&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)     __isctype((c), _ISalnum) // &amp;lt;- this is it
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hey, there’s that implementation detail from earlier! Remember this?&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;enum&lt;/span&gt;
{
  &lt;span class=&quot;constant variable&quot;&gt;_ISupper&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;_ISbit&lt;/span&gt; (&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;),        &lt;span class=&quot;comment&quot;&gt;/* UPPERCASE.  */&lt;/span&gt;
  &lt;span class=&quot;constant variable&quot;&gt;_ISlower&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;_ISbit&lt;/span&gt; (&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;),        &lt;span class=&quot;comment&quot;&gt;/* lowercase.  */&lt;/span&gt;
  &lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;constant variable&quot;&gt;_ISalnum&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;_ISbit&lt;/span&gt; (&lt;span class=&quot;number&quot;&gt;11&lt;/span&gt;)        &lt;span class=&quot;comment&quot;&gt;/* Alphanumeric.  */&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s suss out that macro real quick:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;# include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;bits/endian.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;# if&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__BYTE_ORDER&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__BIG_ENDIAN&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#  define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;_ISbit&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;bit&lt;/span&gt;)   (1 &amp;lt;&amp;lt; (bit))
&lt;span class=&quot;keyword&quot;&gt;# else&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;/* __BYTE_ORDER == __LITTLE_ENDIAN */&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#  define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;_ISbit&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;bit&lt;/span&gt;)   ((bit) &amp;lt; 8 ? ((1 &amp;lt;&amp;lt; (bit)) &amp;lt;&amp;lt; 8) : ((1 &amp;lt;&amp;lt; (bit)) &amp;gt;&amp;gt; 8))
&lt;span class=&quot;keyword&quot;&gt;# endif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, for fuck’s sake. Whatever, let’s move on and just assume this is a magic number. The other macro is &lt;code&gt;__isctype&lt;/code&gt;, which is similar to the &lt;code&gt;__isctype_f&lt;/code&gt; we were just looking at a moment ago. Let’s go look at that &lt;code&gt;ifndef __cplusplus&lt;/code&gt; branch again:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#ifndef&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;__cplusplus&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;# define&lt;/span&gt; &lt;span class=&quot;constant variable function_special&quot;&gt;__isctype&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;type&lt;/span&gt;) \
  ((*__ctype_b_loc ())[(int) (c)] &amp;amp; (unsigned short int) type)
&lt;span class=&quot;keyword&quot;&gt;#elif&lt;/span&gt; defined &lt;span class=&quot;constant variable&quot;&gt;__USE_EXTERN_INLINES&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#endif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;…&lt;/p&gt;&lt;p&gt;Well, at least we have a pointer dereference now, that could explain the segfault. What’s &lt;code&gt;__ctype_b_loc&lt;/code&gt;?&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;comment&quot;&gt;/* These are defined in ctype-info.c.
   The declarations here must match those in localeinfo.h.

   In the thread-specific locale model (see `uselocale&amp;apos; in &amp;lt;locale.h&amp;gt;)
   we cannot use global variables for these as was done in the past.
   Instead, the following accessor functions return the address of
   each variable, which is local to the current thread if multithreaded.

   These point into arrays of 384, so they can be indexed by any `unsigned
   char&amp;apos; value [0,255]; by EOF (-1); or by any `signed char&amp;apos; value
   [-128,-1).  ISO C requires that the ctype functions work for `unsigned
   char&amp;apos; values and for EOF; we also support negative `signed char&amp;apos; values
   for broken old programs.  The case conversion arrays are of `int&amp;apos;s
   rather than `unsigned char&amp;apos;s because tolower (EOF) must be EOF, which
   doesn&amp;apos;t fit into an `unsigned char&amp;apos;.  But today more important is that
   the arrays are also used for multi-byte character sets.  */&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;unsigned short &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;__ctype_b_loc&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;)
     &lt;span class=&quot;constant variable&quot;&gt;__THROW&lt;/span&gt; __attribute__ ((&lt;span class=&quot;constant variable&quot;&gt;__const__&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;__int32_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;__ctype_tolower_loc&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;)
     &lt;span class=&quot;constant variable&quot;&gt;__THROW&lt;/span&gt; __attribute__ ((&lt;span class=&quot;constant variable&quot;&gt;__const__&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;__int32_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;__ctype_toupper_loc&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;)
     &lt;span class=&quot;constant variable&quot;&gt;__THROW&lt;/span&gt; __attribute__ ((&lt;span class=&quot;constant variable&quot;&gt;__const__&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is just so, super cool of you, glibc. I just &lt;em&gt;love&lt;/em&gt; dealing with locales. Anyway, my segfaulted process is sitting in gdb, and equipped with all of this information I wrote the following monstrosity:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;(gdb) print ((unsigned int **(*)(void))__ctype_b_loc)()[next]
Cannot access memory at address 0x11dfa68
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Segfault found. Reading that comment again, we see “ISO C requires that the ctype functions work for ‘unsigned char’ values and for EOF”. If we cross-reference that with the specification:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;In all cases [of functions defined by ctype.h,] the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;So the fix is obvious at this point. Okay, fine, my bad. My code is wrong. I apparently cannot just hand a UCS-32 codepoint to isalnum and expect it to tell me if it’s between 0x30-0x39, 0x41-0x5A, or 0x61-0x7A.&lt;/p&gt;&lt;p&gt;But, I’m going to go out on a limb here: maybe isalnum should never cause a program to segfault no matter what input you give it. Maybe because the spec says you &lt;em&gt;can&lt;/em&gt; does not mean you &lt;em&gt;should&lt;/em&gt;. Maybe, just maybe, the behavior of this function should not depend on five macros, whether or not you’re using a C++ compiler, the endianness of your machine, a look-up table, thread-local storage, and two pointer dereferences.&lt;/p&gt;&lt;p&gt;Here’s the musl version as a quick reminder:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isalnum&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isalpha&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isdigit&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isalpha&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; ((&lt;span class=&quot;type&quot;&gt;unsigned&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;|&lt;span class=&quot;number&quot;&gt;32&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;&amp;apos;a&amp;apos;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;isdigit&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)
{
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;unsigned&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;&amp;apos;0&amp;apos;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bye!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-story-of-two-libcs/</link>
        
        <pubDate>Fri, 25 Sep 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-story-of-two-libcs/</guid>
      </item>
    
      <item>
        
        
          <title>TOFU recommendations for Gemini</title>
          <description>
            &lt;p&gt;I will have more to say about &lt;a href=&quot;https://gemini.circumlunar.space/&quot; target=&quot;_blank&quot;&gt;Gemini&lt;/a&gt; in the future, but for now, I wanted to write up some details about one thing in particular: the trust-on-first-use algorithm I implemented for my client, &lt;a href=&quot;https://sr.ht/~sircmpwn/gmni&quot; target=&quot;_blank&quot;&gt;gmni&lt;/a&gt;. I think you should implement this algorithm, too!&lt;/p&gt;&lt;p&gt;First of all, it’s important to note that the Gemini specification explicitly mentions TOFU and the role of self-signed certificates: they are the norm in Geminiland, and if your client does not support them then you’re going to be unable to browse many sites. However, the exact details are left up to the implementation. Here’s what mine does:&lt;/p&gt;&lt;p&gt;First, on startup, it finds the known_hosts file. For my client, this is &lt;code&gt;~/.local/share/gmni/known_hosts&lt;/code&gt; (the exact path is adjusted as necessary per the XDG basedirs specification). Each line of this file represents a known host, and each host has four fields separated by spaces, in this order:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Hostname (e.g. gemini.circumlunar.space)&lt;/li&gt;&lt;li&gt;Fingerprint algorithm (e.g. SHA-512)&lt;/li&gt;&lt;li&gt;Fingerprint, in hexadecimal, with ‘:’ between each octet (e.g. 55:01:D8…)&lt;/li&gt;&lt;li&gt;Unix timestamp of the certificate’s notAfter date&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If a known_hosts entry is encountered with a hashing algorithm you don’t understand, it is disregarded.&lt;/p&gt;&lt;p&gt;Then, when processing a request and deciding whether or not to trust its certificate, take the following steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Verify that the certificate makes sense. Check the notBefore and notAfter dates against the current time, and check that the hostname is correct (including wildcards). Apply any other scrutiny you want, like enforcing a good hash algorithm or an upper limit on the expiration date. If these checks do not pass, the trust state is INVALID, GOTO 5.&lt;/li&gt;&lt;li&gt;Compute the certificate’s fingerprint. Use the entire certificate (in OpenSSL terms, &lt;code&gt;X509_digest&lt;/code&gt; will do this), not just the public key.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Look up the known_hosts record for this hostname. If one is found, but the record is expired, disregard it. If one is found, and the fingerprint does not match, the trust state is UNTRUSTED, GOTO 5. Otherwise, the trust state is TRUSTED. GOTO 7.&lt;/li&gt;&lt;li&gt;The trust state is UNKNOWN. GOTO 5.&lt;/li&gt;&lt;li&gt;Display information about the certficate and its trust state to the user, and prompt them to choose an action, from the following options:&lt;ul&gt;&lt;li&gt;If INVALID, the user’s choices are ABORT or TRUST_TEMPORARY.&lt;/li&gt;&lt;li&gt;If UNKNOWN, the user’s choices are ABORT, TRUST_TEMPORARY, or TRUST_ALWAYS.&lt;/li&gt;&lt;li&gt;If UNTRUSTED, abort the request and display a diagnostic message. The user must manually edit the known_hosts file to correct the issue.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Complete the requested action:&lt;ul&gt;&lt;li&gt;If ABORT, terminate the request.&lt;/li&gt;&lt;li&gt;If TRUST_TEMPORARY, update the session’s list of known hosts.&lt;/li&gt;&lt;li&gt;If TRUST_ALWAYS, append a record to the known_hosts file and update the session’s list of known hosts.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Allow the request to proceed.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;If the trust state is UNKNOWN, instead of requring user input to proceed, the implementation MAY proceed with the request IF the UI displays that a new certificate was trusted and provides a means to review the certificate and revoke that trust.&lt;/p&gt;&lt;p&gt;Note that being signed by a certificate authority in the system trust store is not considered meaningful to this algorithm. Such a cert is TOFU’d all the same.&lt;/p&gt;&lt;p&gt;That’s it! If you have feedback on this approach, please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;send me an email&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;My implementation doesn’t &lt;em&gt;entirely&lt;/em&gt; match this behavior, but it’s close and I’ll finish it up before 1.0. If you want to read the code, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gmni/tree/master/src/tofu.c&quot; target=&quot;_blank&quot;&gt;here it is&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Bonus recommendation for servers: you &lt;strong&gt;should&lt;/strong&gt; use a self-signed certificate, and you &lt;strong&gt;should not&lt;/strong&gt; use a certificate signed by one of the mainstream certificate authorities. We don’t need to carry along the legacy CA cabal into our brave new Gemini future.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Gemini-TOFU/</link>
        
        <pubDate>Mon, 21 Sep 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Gemini-TOFU/</guid>
      </item>
    
      <item>
        
        
          <title>The unrealized potential of federation</title>
          <description>
            &lt;p&gt;There are some major problems on the internet which may seem intractable. How do we prevent centralization of our communication tools under the authority of a few, whose motivations may not align with our interests? How do we build internet-scale infrastructure without a megacorp-scale budget? Can we make our systems reliable and fault-tolerant — in the face of technical &lt;em&gt;and&lt;/em&gt; social problems?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Federation&lt;/strong&gt; is an idea which takes a swing at all of these problems.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: apparently some cryptocurrency enthusiasts are parading this article around to peddle their garbage. Cryptocurrency is the digitally woke techbro’s ponzi scheme, and is a massive waste of electricity and developer effort. Anyone who tells you anything positive about anything which is even remotely connected to cryptocurrency almost certainly has ulterior motives and you should steer clear. So hopefully that settles that. And cryptocurrency is a P2P system, anyway, NOT a federation!&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The key trait of a software system which is &lt;em&gt;federated&lt;/em&gt; is that the servers are controlled by independent, sovereign entities, and that they exist together under a common web of communication protocols and social agreements. This occupies a sort of middle ground between the centralized architecture and the peer-to-peer (or “decentralized”) architecture. Federation enjoys the advantages of both, and few of the drawbacks.&lt;/p&gt;&lt;p&gt;In a federated software system, groups of users are built around small, neighborly instances of servers. These are usually small servers, sporting only modest resource requirements to support their correspondingly modest userbase. Crucially, these small servers speak to &lt;em&gt;one another&lt;/em&gt; using standard protocols, allowing users of one instance to communicate seamlessly with users of other instances. You can build a culture and shared sense of identity on your instance, but also reach out and easily connect with other instances.&lt;/p&gt;&lt;p&gt;The governance of a federated system then becomes distributed among many operators. Every instance has the following privileges:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;To set the rules which govern users of their instance&lt;/li&gt;&lt;li&gt;To set the rules which govern who they federate with&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;And, because there are hundreds or even thousands of instances, the users get the privilege of choosing an instance whose rules they like, and which federates with other instances they wish to talk to. This system also makes it hard for marketing and spam to get a foothold — it optimizes for a self-governing system of human beings talking to human beings, and not for corporations to push their products.&lt;/p&gt;&lt;p&gt;The costs of scaling up a federation is distributed manageably among these operators. Small instances, with their modest server requirements, are often cheap enough that a sysadmin can comfortably pay for the expenses out of pocket. If not, it’s usually quite easy to solicit donations from the users to keep things running. New operators appear all the time, and the federation scales up a little bit more.&lt;/p&gt;&lt;p&gt;Unlike P2P systems, the federated model allows volunteer sysadmins to use their skills to expand access to the service to non-technical users, without placing the burden on those non-technical users to set up, understand, maintain, or secure servers or esoteric software. The servers are also always online and provide strong identities and authenticity guarantees — eliminating an entire class of P2P problems.&lt;/p&gt;&lt;p&gt;A popular up-and-coming protocol for federation is ActivityPub, but it’s not the only way to build a federated system. You’re certainly familiar with another federation which is not based on ActivityPub: email. IRC and Matrix also provide federated protocols in the instant messaging domain. Personally, I don’t like ActivityPub, but AP is not necessary to reap the benefits of federation. Many different kinds of communication systems can be designed with federation in mind, and adjust their approach to accommodate their specific needs, evident in each of these examples.&lt;/p&gt;&lt;p&gt;In short, federation distributes governance and cost, and can allow us to tackle challenges that we couldn’t overcome without it. The free software community needs to rally behind federation, because no one else will. For all of the reasons which make it worth doing, it is not rewarding for corporations.  They would much rather build walled gardens and centralize, centralize, centralize — it’s more profitable!  Democratic software which puts control into the hands of the users is something we’re going to have to take for ourselves. Viva la federación!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-potential-of-federation/</link>
        
        <pubDate>Sun, 20 Sep 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-potential-of-federation/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, September 2020</title>
          <description>
            &lt;p&gt;A mercifully cool September is upon us, and after years of searching, I finally was able to secure Club Mate in the US. Let’s decant a bottle and recant the story of this month’s progress in free software development.&lt;/p&gt;&lt;p&gt;First of all, I’ve been able to put a pin on operations work on SourceHut for the time being, and focus again on its software development. The GraphQL APIs are a major focus area here, and I’ve made a lot of progress towards OAuth 2.0 support and writable GraphQL APIs. Additionally, I’ve laid out a number of prioritized tickets for the beta — with the “beta” label on todo.sr.ht — and have been picking items off of the list one at a time, mainly focusing on meta.sr.ht improvements at first. I’ll go into more detail in the What’s Cooking post for SourceHut later today, stay tuned.&lt;/p&gt;&lt;p&gt;There has been some advancements in the little projects: a second Python implementation of BARE has appeared, another in Common Lisp, and one in PHP; bringing the total implementations to nine. We have a pretty decent spread of support among programming languages!&lt;/p&gt;&lt;p&gt;Not much more news to share today. Been focusing in on SourceHut and a secret project, so check out the What’s Cooking post for more details. Thanks for your support!&lt;/p&gt;&lt;details&gt;
&lt;summary&gt;...&lt;/summary&gt;
&lt;img
  src=&quot;https://cmpwn.com/system/media_attachments/files/000/970/411/original/f5d4c6f553a5a0ce.png?1599509919&quot;
  alt=&quot;A screenshot of a programming language with a DRAFT watermark. The page shown describes the syntax and semantics of enum types.&quot;
&gt;
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-September-2020/</link>
        
        <pubDate>Tue, 15 Sep 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-September-2020/</guid>
      </item>
    
      <item>
        
        
          <title>Linux development is distributed - profoundly so</title>
          <description>
            &lt;p&gt;The standard introduction to git starts with an explanation of what it means to use a “distributed” version control system. It’s pointed out that every developer has a complete local copy of the repository and can work independently and offline, often contrasting this design with systems like SVN and CVS.  The explanation usually stops here. If you want to learn more, consider git’s roots: it is the version control system purpose-built for Linux, the largest and most active open source project in the world. To learn more about the true nature of distributed development, we should observe Linux.&lt;/p&gt;&lt;p&gt;Pull up your local copy of the Linux source code (you have one of those, right?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;) and open the MAINTAINERS file. Scroll down to line 150 or so and let’s start reading some of these entries.&lt;/p&gt;&lt;p&gt;Each of these represents a different individual or group which has some interest in the Linux kernel, often a particular driver. Most of them have an “F” entry, which indicates which files they’re responsible for in the source code. Most have an “L” entry, which has a mailing list you can post questions, bug reports, and patches to, as well as an individual maintainer (“M”) or maintainers who are known to have expertise and autonomy over this part of the kernel. Many of them — but, hmm, not all — also have a tree (“T”), which is a dedicated git repo with their copy of Linux, for staging changes to the kernel. This is common with larger drivers or with “meta” organizations, which oversee development of entire subsystems.&lt;/p&gt;&lt;p&gt;However, this presents a simplified view. Look carefully at the “DRM” drivers (&lt;a href=&quot;https://en.wikipedia.org/wiki/Direct_Rendering_Manager&quot; target=&quot;_blank&quot;&gt;Direct Rendering Manager&lt;/a&gt;); a group of drivers and maintainers who are collectively responsible for graphics on Linux. There are many drivers and many maintainers, but a careful eye will notice that there are many similarities as well. A lot of them use the same mailing list, &lt;a href=&quot;mailto:dri-devel@lists.freedesktop.org&quot; target=&quot;_blank&quot;&gt;dri-devel@lists.freedesktop.org&lt;/a&gt;, and many of them use the same git repository: &lt;code&gt;git://anongit.freedesktop.org/drm/drm-misc&lt;/code&gt;. It’s not mentioned in this file, but many of them also shared the FreeDesktop bugzilla until recently, then moved to the FreeDesktop GitLab; and many of them share the &lt;code&gt;#dri-devel&lt;/code&gt; IRC channel on Freenode. And again I’m simplifying — there are also many related IRC channels and git repos, and some larger drivers like AMDGPU have dedicated mailing lists and trees.&lt;/p&gt;&lt;p&gt;There’s more complexity to this system still. For example, not all of these subsystems are using git. The Intel TXT subsystem uses Mercurial. The Device Mapper team (one of the largest and most important Linux subsystems) uses &lt;a href=&quot;https://savannah.nongnu.org/projects/quilt&quot; target=&quot;_blank&quot;&gt;Quilt&lt;/a&gt;. And like Linux DRM is a meta-project for many DRM-related subsystems &amp; drivers, there are higher-level meta projects still, such as driver-core, which manages code and subsystems common to &lt;em&gt;all&lt;/em&gt; I/O drivers. There are also cross-cutting concerns, such as the interaction between linux-usb and various network driver teams.&lt;/p&gt;&lt;p&gt;Patches to any particular driver could first end up on a domain-specific mailing list, with a particular maintainer being responsible for reviewing and integrating the patch, with their own policies and workflows and tooling. Then it might flow upwards towards another subsystem with its own similar features, and then up again towards meta-meta trees like linux-staging, and eventually to Linus’ tree&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Along the way it might receive feedback from other projects if it has cross-cutting concerns, tracing out an ever growing and shrinking bubble of inclusion among the trees, ultimately ending up in every tree. And that’s &lt;em&gt;still&lt;/em&gt; a simplification — for example, an important bug fix may sidestep all of this entirely and get applied on top of a downstream distribution kernel, ending up on end-user machines before it’s made much progress upstream at all.&lt;/p&gt;&lt;p&gt;This complex &lt;em&gt;graph&lt;/em&gt; of Linux development has code flowing smoothly between hundreds of repositories, emails exchanging between hundreds of mailing lists, passing through the hands of dozens of maintainers, several bug trackers, various CI systems, all day, every day, ten-thousand fold. This is truly illustrative of &lt;strong&gt;distributed&lt;/strong&gt; software development, well above and beyond the typical explanation given to a new git user. The profound potential of the distributed git system can be plainly seen in the project for which it was principally designed. It’s also plain to see how difficult it would be to adapt this system to something like GitHub pull requests, despite how easy many who are perplexed by the email-driven workflow wish it to be&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. As a matter of fact, several Linux teams are already using GitHub and GitLab and even pull or merge requests on their respective platforms.  However, scaling this system up to the entire kernel would be a great challenge indeed.&lt;/p&gt;&lt;p&gt;By the way — that MAINTAINERS file? Scroll to the bottom. My copy is &lt;em&gt;19,000 lines long&lt;/em&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Linux-development-is-profoundly-distributed/</link>
        
        <pubDate>Wed, 02 Sep 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Linux-development-is-profoundly-distributed/</guid>
      </item>
    
      <item>
        
        
          <title>Linux development is distributed - profoundly so</title>
          <description>
            &lt;p&gt;The standard introduction to git starts with an explanation of what it means to use a “distributed” version control system. It’s pointed out that every developer has a complete local copy of the repository and can work independently and offline, often contrasting this design with systems like SVN and CVS.  The explanation usually stops here. If you want to learn more, consider git’s roots: it is the version control system purpose-built for Linux, the largest and most active open source project in the world. To learn more about the true nature of distributed development, we should observe Linux.&lt;/p&gt;&lt;p&gt;Pull up your local copy of the Linux source code (you have one of those, right?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;) and open the MAINTAINERS file. Scroll down to line 150 or so and let’s start reading some of these entries.&lt;/p&gt;&lt;p&gt;Each of these represents a different individual or group which has some interest in the Linux kernel, often a particular driver. Most of them have an “F” entry, which indicates which files they’re responsible for in the source code. Most have an “L” entry, which has a mailing list you can post questions, bug reports, and patches to, as well as an individual maintainer (“M”) or maintainers who are known to have expertise and autonomy over this part of the kernel. Many of them — but, hmm, not all — also have a tree (“T”), which is a dedicated git repo with their copy of Linux, for staging changes to the kernel. This is common with larger drivers or with “meta” organizations, which oversee development of entire subsystems.&lt;/p&gt;&lt;p&gt;However, this presents a simplified view. Look carefully at the “DRM” drivers (&lt;a href=&quot;https://en.wikipedia.org/wiki/Direct_Rendering_Manager&quot; target=&quot;_blank&quot;&gt;Direct Rendering Manager&lt;/a&gt;); a group of drivers and maintainers who are collectively responsible for graphics on Linux. There are many drivers and many maintainers, but a careful eye will notice that there are many similarities as well. A lot of them use the same mailing list, &lt;a href=&quot;mailto:dri-devel@lists.freedesktop.org&quot; target=&quot;_blank&quot;&gt;dri-devel@lists.freedesktop.org&lt;/a&gt;, and many of them use the same git repository: &lt;code&gt;git://anongit.freedesktop.org/drm/drm-misc&lt;/code&gt;. It’s not mentioned in this file, but many of them also shared the FreeDesktop bugzilla until recently, then moved to the FreeDesktop GitLab; and many of them share the &lt;code&gt;#dri-devel&lt;/code&gt; IRC channel on Freenode. And again I’m simplifying — there are also many related IRC channels and git repos, and some larger drivers like AMDGPU have dedicated mailing lists and trees.&lt;/p&gt;&lt;p&gt;There’s more complexity to this system still. For example, not all of these subsystems are using git. The Intel TXT subsystem uses Mercurial. The Device Mapper team (one of the largest and most important Linux subsystems) uses &lt;a href=&quot;https://savannah.nongnu.org/projects/quilt&quot; target=&quot;_blank&quot;&gt;Quilt&lt;/a&gt;. And like Linux DRM is a meta-project for many DRM-related subsystems &amp; drivers, there are higher-level meta projects still, such as driver-core, which manages code and subsystems common to &lt;em&gt;all&lt;/em&gt; I/O drivers. There are also cross-cutting concerns, such as the interaction between linux-usb and various network driver teams.&lt;/p&gt;&lt;p&gt;Patches to any particular driver could first end up on a domain-specific mailing list, with a particular maintainer being responsible for reviewing and integrating the patch, with their own policies and workflows and tooling. Then it might flow upwards towards another subsystem with its own similar features, and then up again towards meta-meta trees like linux-staging, and eventually to Linus’ tree&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Along the way it might receive feedback from other projects if it has cross-cutting concerns, tracing out an ever growing and shrinking bubble of inclusion among the trees, ultimately ending up in every tree. And that’s &lt;em&gt;still&lt;/em&gt; a simplification — for example, an important bug fix may sidestep all of this entirely and get applied on top of a downstream distribution kernel, ending up on end-user machines before it’s made much progress upstream at all.&lt;/p&gt;&lt;p&gt;This complex &lt;em&gt;graph&lt;/em&gt; of Linux development has code flowing smoothly between hundreds of repositories, emails exchanging between hundreds of mailing lists, passing through the hands of dozens of maintainers, several bug trackers, various CI systems, all day, every day, ten-thousand fold. This is truly illustrative of &lt;strong&gt;distributed&lt;/strong&gt; software development, well above and beyond the typical explanation given to a new git user. The profound potential of the distributed git system can be plainly seen in the project for which it was principally designed. It’s also plain to see how difficult it would be to adapt this system to something like GitHub pull requests, despite how easy many who are perplexed by the email-driven workflow wish it to be&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. As a matter of fact, several Linux teams are already using GitHub and GitLab and even pull or merge requests on their respective platforms.  However, scaling this system up to the entire kernel would be a great challenge indeed.&lt;/p&gt;&lt;p&gt;By the way — that MAINTAINERS file? Scroll to the bottom. My copy is &lt;em&gt;19,000 lines long&lt;/em&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Linux-development-is-profoundly-distribute/</link>
        
        <pubDate>Wed, 02 Sep 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Linux-development-is-profoundly-distribute/</guid>
      </item>
    
      <item>
        
        
          <title>Embrace, extend, and finally extinguish - Microsoft plays their hand</title>
          <description>
            &lt;p&gt;GitHub took a note out of the Microsoft “&lt;abbr title=&quot;Embrace, Extend,
Extinguish&quot;&gt;EEE&lt;/abbr&gt;” playbook when designing their git services. They &lt;strong&gt;embraced&lt;/strong&gt; git, and then rather than building an interface on top of email — the collaboration mechanism that git was designed to use, and which is still used for Linux kernel development&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; — they built their “pull requests” mechanism.&lt;/p&gt;&lt;p&gt;They took terminology which already had meaning — “fork”, meaning the creation a separate governing body and development upstream for a codebase, a rather large task; and “pull request”, a git workflow which prepares an email asking a receipient to pull a large branch of changes from a non-centralized source — and replaced these decentralized, open systems with a completely incompatible system designed to keep you on GitHub and to teach you to collaborate using GitHub’s proprietary tools. They &lt;strong&gt;extended&lt;/strong&gt; git in a proprietary way.&lt;/p&gt;&lt;p&gt;Microsoft knows a good deal when they see one, and picked up GitHub for a cool $7,500,000,000, after they had already completed the two steps in Microsoft’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish&quot; target=&quot;_blank&quot;&gt;anti-open-source playbook&lt;/a&gt;. They joined the Linux Foundation in late 2016, after Azure failed to win people back to Windows Server, admitting defeat while simultaneously carving out a space from which they could project their interests over the kernel.&lt;/p&gt;&lt;p&gt;Today, I discovered this article, “&lt;a href=&quot;https://www.theregister.com/2020/08/25/linux_kernel_email/&quot; target=&quot;_blank&quot;&gt;Relying on plain-text email is a ‘barrier to entry’ for kernel development, says Linux Foundation board member&lt;/a&gt;”, a title which conveniently chooses to refer to Sarah Novotny by her role as a Linux Foundation board member, rather than by her full title, “Sarah Novotny, Microsoft employee, transitive owner of GitHub, and patroness saint of conflicts of interests.” Finally, they’re playing the &lt;strong&gt;extinguish&lt;/strong&gt; card. Naturally, a representative of Microsoft, a company which has long waged war against open source, and GitHub, a company which explicitly built an incompatible proprietary system to extend git, would have an interest in dismantling the distributed, open system that git was designed for.&lt;/p&gt;&lt;p&gt;I represent &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;sourcehut&lt;/a&gt;, a GitHub competitor which does what GitHub wouldn’t — interoperate with open, distributed protocols, and in the form of 100% free and open-source software. I agree that the UX of email-driven development could be better! But instead of investing $7.5B into throwing the baby out with the bathwater, we’ve &lt;a href=&quot;https://git-send-email.io/&quot; target=&quot;_blank&quot;&gt;built interactive tutorials&lt;/a&gt;, &lt;a href=&quot;https://lists.sr.ht/~emersion/mrsh-dev/patches/4728&quot; target=&quot;_blank&quot;&gt;designed better mailing lists&lt;/a&gt;, &lt;a href=&quot;https://sr.ht/_fUk.webm&quot; target=&quot;_blank&quot;&gt;built web interfaces for patch submission&lt;/a&gt;, &lt;a href=&quot;https://sourcehut.org/blog/2020-07-14-setting-up-ci-for-mailing-lists/&quot; target=&quot;_blank&quot;&gt;implemented CI for emails&lt;/a&gt; and &lt;a href=&quot;https://github.com/git/git/commits?author=ddevault&quot; target=&quot;_blank&quot;&gt;sent improvements to git upstream&lt;/a&gt;. I wrote &lt;a href=&quot;https://aerc-mail.org/&quot; target=&quot;_blank&quot;&gt;an entire mail client which makes it easier to use these tools&lt;/a&gt;. We’re planning on web-based review interface, too. The result is a UX which provides a similar experience to GitHub, but without disrupting the established open ecosystem.&lt;/p&gt;&lt;p&gt;&lt;em&gt;This&lt;/em&gt; is how you improve the ecosystem, Microsoft. Take notes. Stick with the embrace, move your extending &lt;em&gt;upstream&lt;/em&gt;, and forget about extinguish.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Microsoft-plays-their-hand/</link>
        
        <pubDate>Thu, 27 Aug 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Microsoft-plays-their-hand/</guid>
      </item>
    
      <item>
        
        
          <title>Alice in Wonderland and the theft of the public domain</title>
          <description>
            &lt;p&gt;Disney’s &lt;em&gt;Alice in Wonderland&lt;/em&gt; is one of my favorite movies and an undisputed classic. After its release in 1951, &lt;em&gt;Alice&lt;/em&gt; holds a fond place in billions of children’s hearts, over almost four generations. And it has been stolen from those generations, as part of the theft of one of these generations’ greatest treasures: the public domain.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://redacted.moe/f/5e5f11ef.jpg&quot;&gt;&lt;/p&gt;&lt;p&gt;I often use this film as an example when arguing about copyright. Almost everyone I speak to was born well after the film’s release (in fact, this is true of almost everyone &lt;em&gt;alive today&lt;/em&gt;), but they remember it fondly regardless. Many people I’ve spoken to would agree that it even played a formative role in their childhoods; it’s a film dear to many hearts. My mom is very fond of the Cheshire Cat in particular, and owns quite a bit of relevant merchandise.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Like many films from their “Golden Age”, Disney’s &lt;em&gt;Alice&lt;/em&gt; is itself a derivative work, based on Lewis Carroll’s 1865 book. However, Disney’s film won’t enter the public domain until 2046, and until then, no one can create derivative works of their own without receiving permission from and paying a tithe to Disney. And if modern-day copyright law, &lt;a href=&quot;https://en.wikipedia.org/wiki/Copyright_Term_Extension_Act&quot; target=&quot;_blank&quot;&gt;bought and paid for by Disney&lt;/a&gt;, had been in force at the time Alice in Wonderland was made, they would have released their film 17 years &lt;em&gt;before&lt;/em&gt; Carroll’s novel entered the public domain.&lt;/p&gt;&lt;p&gt;Carroll, who died in 1898, was 53 years dead when the film was released. Everyone who is listed in the credits for Disney’s Alice in Wonderland is also dead, with the exception of Kathryn Beaumont, who played the role of none other than Alice herself.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; She was 12 years old at the time. And still today, the copyright remains in force, though no creators remain to enjoy its privileges. It shall remain so for another 26 years, when I can finally celebrate my Alice-in-Wonderland-themed 53rd birthday party, having been robbed of the privilege at age 11.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Copyright was established in the United States to incentivize artists, musicians, authors, writers, and other creatives to create novel art, allowing them to enjoy the exclusive rights to it for a short period&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, then ultimately &lt;em&gt;enriching&lt;/em&gt; the public domain. The obscene copyright terms we’re faced with today have robbed the American public of its national heritage. Any work made today will not enter the public domain during the lifetimes of any of its contemporaries, let alone soon enough for those contemporaries to &lt;em&gt;do&lt;/em&gt; anything with it.&lt;/p&gt;&lt;p&gt;A system designed to incentivize creation has become a system which incentivises the opposite: rent seeking. A rent which is sought from the American public, in exchange for which we’re no longer getting our end of the deal.&lt;/p&gt;&lt;p&gt;Well, the deal is off.&lt;/p&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;/video-js.css&quot;&gt;
&lt;script&gt;
window.HELP_IMPROVE_VIDEOJS = false;
&lt;/script&gt;
&lt;script src=&quot;/video.js&quot;&gt;&lt;/script&gt;

&lt;video class=&quot;video-js vjs-16-9&quot; data-setup=&quot;{}&quot; controls&gt;
  &lt;source src=&quot;https://drewdevault.com/alice.webm&quot; type=&quot;video/webm&quot;&gt;
  &lt;p&gt;Your browser does not support HTML5 video, or webm. Either way you&apos;re not
  going to watch this video.&lt;/p&gt;
&lt;/video&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Alice-in-Wonderland/</link>
        
        <pubDate>Mon, 24 Aug 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Alice-in-Wonderland/</guid>
      </item>
    
      <item>
        
        
          <title>Software engineers solve problems</title>
          <description>
            &lt;p&gt;Software engineers solve problems. A problem you may have encountered is, for example, “this function has a bug”, and you’re probably already more or less comfortable solving these problems. Here are some other problems you might encounter on the way:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Actually, the bug ultimately comes from a third-party program&lt;/li&gt;&lt;li&gt;Hm, it uses a programming language I don’t know&lt;/li&gt;&lt;li&gt;Oh, the bug is in that programming language’s compiler&lt;/li&gt;&lt;li&gt;This subsystem of the compiler would have to be overhauled&lt;/li&gt;&lt;li&gt;And the problem is overlooked by the language specification&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I’ve met many engineers who, when standing at the base of this mountain, conclude that the summit is too far away and clearly not their responsibility, and subsequently give up. But remember: as an engineer, your job is to apply creativity to solving problems. Are these not themselves problems to which the engineering process may be applied?&lt;/p&gt;&lt;p&gt;You can introduce yourself to the maintainers of the third-party program and start working on a solution. You can study the programming language you don’t know, at least as much as is necessary to understand and correct the bug. You can read the compiler’s source code, and identify the subsystem which needs overhauling, then introduce yourself to &lt;em&gt;those&lt;/em&gt; maintainers and work on the needed overhaul. The specification is probably managed by a working group, reach out to them and have an erratta issued or a clarification added to the upcoming revision.&lt;/p&gt;&lt;p&gt;The scope of fixing this bug is broader than you thought, but if you apply a deliberate engineering process to each problem that you encounter, eventually you will complete the solution. This process of recursively solving problems to get at the one you want to solve is called “&lt;a href=&quot;http://catb.org/jargon/html/Y/yak-shaving.html&quot; target=&quot;_blank&quot;&gt;yak shaving&lt;/a&gt;”, and it’s a necessary part of your workflow.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Engineers-solve-problems/</link>
        
        <pubDate>Mon, 17 Aug 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Engineers-solve-problems/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, August 2020</title>
          <description>
            &lt;p&gt;Greetings! Today is another rainy day here in Philadelphia, which rather sours my plans of walking over to the nearby cafe to order some breakfast to-go. But I am tired, and if I’m going to make it to the end of this blog post in one piece, I’m gonna need a coffee. brb.&lt;/p&gt;&lt;p&gt;Hey, that was actually pretty refreshing. It’s just drizzling, and the rain is nice and cool. Alright, here goes! What’s new? I’ll leave the Wayland news for &lt;a href=&quot;https://emersion.fr/blog&quot; target=&quot;_blank&quot;&gt;Simon Ser’s blog&lt;/a&gt; this month - he’s been working on some exciting stuff. The &lt;a href=&quot;https://baremessages.org/&quot; target=&quot;_blank&quot;&gt;BARE encoding&lt;/a&gt; announced last month has received some great feedback and refinements, and there are now six projects providing BARE support for their author’s favorite programming language&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. There have also been some improvements to the Go implementation which should help with some SourceHut plans later on.&lt;/p&gt;&lt;p&gt;On the subject of SourceHut, I’ve focused mainly on infrastructure improvements this month. There is a new server installed for hg.sr.ht, which will also be useful as a testbed for additional ops work planned for future expansion. Additionally, the PostgreSQL backup system has been overhauled and made more resilient, both to data loss and to outages. A lot of other robustness improvements have been made fleet-wide in monitoring. I’ll be working on more user-facing features again next month, but in the meanwhile, contributors like наб have sent many patches in which I’ll cover in detail in the coming “What’s cooking” post for &lt;a href=&quot;https://sourcehut.org/blog&quot; target=&quot;_blank&quot;&gt;sourcehut.org&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Otherwise, I’ve been taking it easy this month. I definitely haven’t been spending a lot of my time on a secret project, no sir. Thanks again for your support! I’ll see you next month.&lt;/p&gt;&lt;details&gt;
&lt;summary&gt;?&lt;/summary&gt;
&lt;pre&gt;
use io;
use io_uring = linux::io_uring;
use linux;
use strings;

export fn main void = {
	let uring = match (io_uring::init(256u32, 0u32)) {
		err: linux::error =&amp;gt; {
			io::println(&quot;io_uring::init error:&quot;);
			io::println(linux::errstr(err));
			return;
		},
		u: io_uring::io_uring =&amp;gt; u,
	};

	let buf: [8192]u8 = [0u8...];
	let text: nullable *str = null;
	let wait = 0u;
	let offs = 0z;
	let read: *io_uring::sqe = null: *io_uring::sqe,
		write: *io_uring::sqe = null: *io_uring::sqe;
	let eof = false;

	while (!eof) {
		read = io_uring::must_get_sqe(&amp;amp;uring);
		io_uring::prep_read(read, linux::STDIN_FILENO,
			&amp;amp;buf, len(buf): u32, offs);
		io_uring::sqe_set_user_data(read, &amp;amp;read);
		wait += 1u;

		let ev = match (io_uring::submit_and_wait(&amp;amp;uring, wait)) {
			err: linux::error =&amp;gt; {
				io::println(&quot;io_uring::submit error:&quot;);
				io::println(linux::errstr(err));
				return;
			},
			ev: uint =&amp;gt; ev,
		};

		wait -= ev;

		for (let i = 0; i &amp;lt; ev; i += 1) {
			let cqe = match (io_uring::get_cqe(&amp;amp;uring, 0u, 0u)) {
				err: linux::error =&amp;gt; {
					io::println(&quot;io_uring::get_cqe error:&quot;);
					io::println(linux::errstr(err));
					return;
				},
				c: *io_uring::cqe =&amp;gt; c,
			};

			if (io_uring::cqe_get_user_data(cqe) == &amp;amp;read) {
				if (text != null) {
					free(text);
				};

				if (cqe.res == 0) {
					eof = true;
					break;
				};

				text = strings::must_decode_utf8(buf[0..cqe.res]);
				io_uring::cqe_seen(&amp;amp;uring, cqe);

				write = io_uring::must_get_sqe(&amp;amp;uring);
				io_uring::prep_write(write, linux::STDOUT_FILENO,
					text: *char, len(text): u32, 0);
				io_uring::sqe_set_user_data(write, &amp;amp;write);
				wait += 1u;
				offs += cqe.res;
			} else if (io_uring::cqe_get_user_data(cqe) == &amp;amp;write) {
				assert(cqe.res &amp;gt; 0);
				io_uring::cqe_seen(&amp;amp;uring, cqe);
			} else {
				assert(false, &quot;Unknown CQE user data&quot;);
			};
		};
	};

	io_uring::close(&amp;amp;uring);
};
&lt;/pre&gt;

&lt;details&gt;
&lt;summary&gt;hmm?&lt;/summary&gt;
&lt;p&gt;I might note that I wrote this program to test my io_uring wrapper; it&apos;s not
representative of how normal programs will do I/O in the future.&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update/</link>
        
        <pubDate>Sun, 16 Aug 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update/</guid>
      </item>
    
      <item>
        
        
          <title>Web browsers need to stop</title>
          <description>
            &lt;p&gt;Enough is enough.&lt;/p&gt;&lt;p&gt;The web and web browsers have become Lovecraftian horrors of an unprecedented scale. They’ve long since left “scope creep” territory and entered “oh my god please just stop” territory, and are trucking on through to hitherto unexplored degrees of &lt;em&gt;obscene&lt;/em&gt; scope. And we &lt;em&gt;don’t want&lt;/em&gt; what they’re selling. Google pitches garbage like AMP&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and pushing dubious half-assed specs like Web Components. Mozilla just fired everyone relevant&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; to focus on crap no one asked for like Pocket, and fad nonsense like a paid VPN service and &lt;del&gt;virtual reality tech&lt;/del&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;em&gt;[2020-08-14: It has been pointed out that the VR team was also fired.]&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Microsoft gave up entirely. Mozilla just hammered the last few nails into their casket.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;del&gt;Safari is a joke&lt;/del&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. Google is all that’s left, and they’re &lt;em&gt;not&lt;/em&gt; a good steward of the open web. The browsers are drowning under their own scope.  The web is dead.&lt;/p&gt;&lt;p&gt;I call for an immediate and indefinite suspension of the addition of new developer-facing APIs to web browsers. Browser vendors need to start thinking about &lt;em&gt;reducing&lt;/em&gt; scope and &lt;em&gt;cutting&lt;/em&gt; features. WebUSB, WebBluetooth, WebXR, &lt;del&gt;WebDRM&lt;/del&gt; &lt;del&gt;WebMPAA&lt;/del&gt; &lt;del&gt;WebBootlicking&lt;/del&gt; &lt;del&gt;replacing User-Agent with Vendor-Agent cause let’s be honest with ourselves at this point&lt;/del&gt; “Encrypted Media Extensions” — this crap all needs to go. At some point you need to stop adding scope and start focusing on performance, efficiency, reliability, and security&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; at the scope you already have.&lt;/p&gt;&lt;p&gt;Enough is enough.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Web-browsers-need-to-stop/</link>
        
        <pubDate>Thu, 13 Aug 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Web-browsers-need-to-stop/</guid>
      </item>
    
      <item>
        
        
          <title>I want to contribute to your project, how do I start?</title>
          <description>
            &lt;p&gt;I get this question a lot! The answer is usually… don’t. If you already know what you want to do, then the question doesn’t need to be asked.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; But, if you don’t already know what you want to do, then your time might be better spent elsewhere!&lt;/p&gt;&lt;p&gt;The best contributors are always intrinsically motivated. Some contributors show up every now and then who appreciate the value the project gives to them and want to give something back. Their gratitude is definitely appreciated&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, but these kinds of contributions tend to require more effort from the maintainers, and don’t generally lead to recurring contributions. Projects you already like are less likely to need help when compared to incomplete projects that you don’t already depend on — so this model leaves newer projects with fewer contributors and encourages established projects to grow in complexity.&lt;/p&gt;&lt;p&gt;Instead, you should focus on scratching your own itches. Is there a bug which is getting on your nerves? A conspicuously absent feature? Work on those!&lt;/p&gt;&lt;p&gt;If there’s nothing specific that you want to work on, then you may be better off finding something to do in a different project. Don’t be afraid to work on any free- and open-source codebase that you encounter — nearly all of them will accept your patches. If something is bothering you about another project, then go fix it! Someone has a cool idea and needs help realizing it? Get involved! If we spread the contributions around, the FOSS ecosystem will flourish and the benefits will come back around to our project, too.&lt;/p&gt;&lt;p&gt;So, if you want to contribute to open-source — as a whole — here are my tips:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Find problems which you are intrinsically motivated to work on.&lt;/li&gt;&lt;li&gt;Focus on developing skills to get up to speed on new codebases fast.&lt;/li&gt;&lt;li&gt;Don’t be afraid to work on &lt;em&gt;any&lt;/em&gt; project — new languages, tools, libraries; learn enough of them and it’ll only get easier to learn more.&lt;/li&gt;&lt;li&gt;When you file bug reports with a FOSS project, get into the habit of following up with a patch which addresses the problem.&lt;/li&gt;&lt;li&gt;Get used to introducing yourself to maintainers and talking through the code; it always pays to ask.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you want to work on a specific project, and you have a specific goal in mind: perfect! If you don’t have a specific goal in mind, try to come up with some. And if you’re still drawing a blank, consider another project.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-contribute-to-FOSS/</link>
        
        <pubDate>Mon, 10 Aug 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-contribute-to-FOSS/</guid>
      </item>
    
      <item>
        
        
          <title>pkg.go.dev is more concerned with Google&apos;s interests than good engineering</title>
          <description>
            &lt;p&gt;pkg.go.dev sucks. It’s certainly &lt;em&gt;prettier&lt;/em&gt; than godoc.org, but under the covers, it’s a failure of engineering characteristic of the Google approach.&lt;/p&gt;&lt;p&gt;Go is a &lt;em&gt;pretty good&lt;/em&gt; programming language. I have long held that this is not attributable to Google’s stewardship, but rather to a small number of language designers and a clear line of influences which is drawn entirely from outside of Google — mostly from Bell Labs. pkg.go.dev provides renewed support for my argument: it has all the hallmarks of Google crapware and none of the deliberate, good engineering work that went into Go’s design.&lt;/p&gt;&lt;p&gt;It was apparent from the start that this is what it would be. pkg.go.dev was launched as a closed-source product, &lt;a href=&quot;https://blog.golang.org/pkg.go.dev-2020&quot; target=&quot;_blank&quot;&gt;justified&lt;/a&gt; by pointing out that godoc.org is too complex to run on an intranet, and pkg.go.dev has the same problem. There are many problems to take apart in this explanation: the assumption that the only reason an open source platform is desirable is for running it on your intranet; the unstated assumption that such complexity is necessary or agreeable in the first place; and the &lt;a href=&quot;https://github.com/golang/go/issues/25443&quot; target=&quot;_blank&quot;&gt;systemic&lt;/a&gt; &lt;a href=&quot;https://github.com/golang/go/issues/30029&quot; target=&quot;_blank&quot;&gt;erosion&lt;/a&gt; of the existing (and simple!) tools which &lt;em&gt;could&lt;/em&gt; have been used for this purpose prior to this change. The attitude towards open source was only changed following pkg.go.dev’s harsh reception by the community.&lt;/p&gt;&lt;p&gt;But this attitude &lt;em&gt;did&lt;/em&gt; change, and it is open-source now&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, so let’s give them credit for that. The good intentions are spoilt by the fact that pkg.go.dev fetches the list of modules from &lt;a href=&quot;https://proxy.golang.org/&quot; target=&quot;_blank&quot;&gt;proxy.golang.org&lt;/a&gt;: a closed-source proxy through which all of your go module fetches are being routed and tracked (oh, you didn’t know? They never told you, after all). Anyway, enough of the gross disregard for the values of open source and user privacy; I &lt;em&gt;do&lt;/em&gt; have some technical problems to talk about.&lt;/p&gt;&lt;p&gt;One concern comes from a blatant failure to comprehend the fundamentally decentralized nature of git hosting. Thankfully, git.sr.ht is supported now&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; — but only &lt;em&gt;the&lt;/em&gt; git.sr.ht, i.e. the hosted instance, not the software. pkg.go.dev hard-codes a list of centralized git hosting services, and completely disregards the idea of git hosting as &lt;em&gt;software&lt;/em&gt; rather than as a &lt;em&gt;platform&lt;/em&gt;. Any GitLab instance other than gitlab.com (such as &lt;a href=&quot;https://gitlab.freedesktop.org&quot; target=&quot;_blank&quot;&gt;gitlab.freedesktop.org&lt;/a&gt; or &lt;a href=&quot;https://salsa.debian.org/public&quot; target=&quot;_blank&quot;&gt;salsa.debian.org&lt;/a&gt;); any &lt;a href=&quot;https://gogs.io/&quot; target=&quot;_blank&quot;&gt;Gogs&lt;/a&gt; or &lt;a href=&quot;https://gitea.io/en-us/&quot; target=&quot;_blank&quot;&gt;Gitea&lt;/a&gt; like &lt;a href=&quot;https://codeberg.org&quot; target=&quot;_blank&quot;&gt;Codeberg&lt;/a&gt;; cgit instances like &lt;a href=&quot;https://git.kernel.org/&quot; target=&quot;_blank&quot;&gt;git.kernel.org&lt;/a&gt;; none of these are going to work unless every host is added and the list is kept up-to-date manually. Your intranet instance of cgit? Not a chance.&lt;/p&gt;&lt;p&gt;They were also given an opportunity here to fix a long-standing problem with Go package discovery, namely that it requires every downstream git repository host has to (1) provide a web interface and (2) include &lt;em&gt;Go-specific&lt;/em&gt; meta tags in the HTML. The hubris to impose your &lt;em&gt;programming language&lt;/em&gt;’s needs onto a language-agnostic version control system! I asked: they have no interest in the better-engineered — but more worksome — approach of pursing a language agnostic design.&lt;/p&gt;&lt;p&gt;The worldview of the developers is whack, the new site introduces dozens of regressions, and all it really improves upon is the visual style — which could trivially have been done to godoc.org. The goal is shipping a shiny new product — not engineering a good solution. This is typical of Google’s engineering ethos in general. pkg.go.dev sucks, and is added the large (and growing) body of evidence that Google is bad for Go.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/pkg-go-dev-sucks/</link>
        
        <pubDate>Sat, 01 Aug 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/pkg-go-dev-sucks/</guid>
      </item>
    
      <item>
        
        
          <title>The falsehoods of anti-AGPL propaganda</title>
          <description>
            &lt;p&gt;Google is well-known for &lt;a href=&quot;https://opensource.google/docs/using/agpl-policy/&quot; target=&quot;_blank&quot;&gt;forbidding the use of&lt;/a&gt; software using the &lt;a href=&quot;https://www.gnu.org/licenses/agpl-3.0.en.html&quot; target=&quot;_blank&quot;&gt;GNU Affero General Public License&lt;/a&gt;, commonly known as “AGPL”. Google is also well-known for being the subject of cargo-culting by fad startups. Unfortunately, this means that they are susceptible to what is ultimately anti-AGPL propaganda from Google, with little to no basis in fact.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Obligatory: I’m not a lawyer; this is for informational purposes only.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;In truth, the terms of the AGPL are pretty easy to comply with. The basic obligations of the AGPL which set it apart from other licenses are as follows:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Any derivative works of AGPL-licensed software must also use the AGPL.&lt;/li&gt;&lt;li&gt;Any users of such software are entitled to the source code under the terms of the AGPL, including users accessing it over the network such as with their web browser or via an API or internet protocol.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you’re using AGPL-licensed software like a database engine or &lt;a href=&quot;https://sr.ht/~sircmpwn/sourcehut/&quot; target=&quot;_blank&quot;&gt;my own AGPL-licensed works&lt;/a&gt;, and you haven’t made any changes to the source code, you don’t have to do anything to comply. If you &lt;em&gt;have&lt;/em&gt; modified the software, you simply have to publish your modifications. The easiest way to do this is to send it as a patch upstream, but you could use something as simple as providing a tarball to your users.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The nuances are detailed and cover many edge cases to prevent abuse. But in general, just publish your modifications under the same AGPL terms and you’ll be good to go. The license is usually present in the source code as a &lt;code&gt;COPYING&lt;/code&gt; or &lt;code&gt;LICENSE&lt;/code&gt; file, so if you just tar up your modified source code and drop a link on your website, that’s good enough. If you want to go the extra mile and express your gratitude to the original software developers, consider submitting your changes for upstream inclusion. Generally, the feedback you’ll receive will help to make your changes better for your use-case, too; and submitting your work upstream will prevent your copy from diverging from upstream.&lt;/p&gt;&lt;p&gt;That’s pretty easy, right? I’m positive that your business has to deal with much more onerous contracts than the AGPL. Then why does Google make a fuss about it?&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://opensource.google/docs/using/agpl-policy/&quot; target=&quot;_blank&quot;&gt;The Google page about the AGPL&lt;/a&gt; details inaccurate (but common&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;) misconceptions about the obligations of the AGPL that don’t follow from the text. Google states that if, for example, Google Maps used PostGIS as its data store, and PostGIS used the AGPL, Google would be required to release the Google Maps code. This is not true. They would be required to release &lt;em&gt;their PostGIS patches&lt;/em&gt; in this situation. AGPL does not extend the GPL in that it makes the Internet count as a form of linking which creates a derivative work, as Google implies, but rather that it makes anyone who uses the software via the Internet entitled to its source code. It does not update the “what counts as a ‘derivative work’” algorithm, so to speak — it updates the “what counts as ‘distributing’ the software” algorithm.&lt;/p&gt;&lt;p&gt;The reason they spread these misconceptions is straightforward: they want to discourage people from using the AGPL, because they cannot productize such software effectively. Google wants to be able to incorporate FOSS software into their products and sell it to users without the obligation to release their derivative works. Google is an Internet company, and they offer Internet services. The original GPL doesn’t threaten their scheme because their software is accessed over the Internet, not distributed to end-users directly.&lt;/p&gt;&lt;p&gt;By discouraging the use of AGPL in the broader community, Google hopes to create a larger set of free- and open-source software that they can take for their own needs without any obligations to upstream. Ask yourself: why is documentation of internal-facing decisions like what software licenses to use being published in a public place? The answer is straightforward: to influence the public. This is propaganda.&lt;/p&gt;&lt;p&gt;There’s a bizarre idea that software companies which eschew the AGPL in favor of something like MIT are doing so specifically because they want companies “like Google&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;” to pay for their software, and they know that they have no chance if they use AGPL. In truth, Google was never going to buy your software. If you don’t use the AGPL, they’re just going to take your software and give nothing back. If you do use the AGPL, they’re just going to develop a solution in-house. There’s no outcome where Google pays you.&lt;/p&gt;&lt;p&gt;Don’t be afraid to use the AGPL, and don’t be afraid to use software which uses the AGPL. The obligations are not especially onerous or difficult, despite what Google would have you believe. The license isn’t that long — read it and see for yourself.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Anti-AGPL-propaganda/</link>
        
        <pubDate>Mon, 27 Jul 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Anti-AGPL-propaganda/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, July 2020</title>
          <description>
            &lt;p&gt;Hello again! Another month of FOSS development behind us, and we’re back again to share the results. I took a week off at the end of June, so my progress this month is somewhat less than usual. Regardless, I have some updates for you, mainly in the domain of SourceHut work.&lt;/p&gt;&lt;p&gt;But before we get to that, let’s go over this month’s small victories. One was the invention of the &lt;a href=&quot;https://baremessages.org&quot; target=&quot;_blank&quot;&gt;BARE message format&lt;/a&gt;, which I wrote &lt;a href=&quot;https://drewdevault.com/2020/06/21/BARE-message-encoding.html&quot; target=&quot;_blank&quot;&gt;a blog post about&lt;/a&gt; if you want to learn more. Since that article, five new implementations have appeared from various authors: Rust, Python, JavaScript, D, and Zig.&lt;/p&gt;&lt;p&gt;I also wrote a couple of not-blogposts for this site (drewdevault.com), including a page &lt;a href=&quot;https://example.org/dynlib&quot; target=&quot;_blank&quot;&gt;dispelling misconceptions about static linking&lt;/a&gt;. Just dropping a link here in case you missed them; they didn’t appear in RSS and aren’t blog posts. To help find random stuff like that on this site, I’ve also established a &lt;a href=&quot;https://drewdevault.com/misc/&quot;&gt;misc page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Okay, on to SourceHut. Perhaps the most exciting development is the addition of &lt;a href=&quot;https://sourcehut.org/blog/2020-07-14-setting-up-ci-for-mailing-lists/&quot; target=&quot;_blank&quot;&gt;continuous integration to the mailing lists&lt;/a&gt;. I’ve been working towards this for some time now, and it’s the first of many features which are now possible thanks to the addition of the project hub. I intend to complete some follow-up work improving the CI feature further still in the coming weeks. I’m also planning an upgrade for the hardware that runs hg.sr.ht during the same timeframe.&lt;/p&gt;&lt;p&gt;That’s all the news I have for now, somewhat less than usual. Some time off was much-needed, though. Thanks for your continued support, and I hope you continue to enjoy using my software!&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;hare&quot;&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable namespace&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;main&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;envp&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; i &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;error keyword&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;from_c&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;envp&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;error constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$ $&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;redacted&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;8&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;41&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;incorrect&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;parameter&lt;/span&gt; 1 &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;char&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;error constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;from_c&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;envp&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;error punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;error punctuation_delimiter&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
                                        &lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;error&quot;&gt; &lt;/span&gt;&lt;span class=&quot;error constant variable&quot;&gt;here&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;vim&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;
&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;include&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword_function&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;main&lt;/span&gt; void &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;repeat&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; 0&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;envp&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; 1&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;from_c&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;envp&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;error&quot;&gt;$ $&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;redacted&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;error&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ext&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;DISPLAY&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;0
&lt;span class=&quot;constant variable&quot;&gt;EDITOR&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;vim&lt;/span&gt;
&lt;span class=&quot;error&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;punctuation_special&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-July-2020/</link>
        
        <pubDate>Wed, 15 Jul 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-July-2020/</guid>
      </item>
    
      <item>
        
        
          <title>March 2nd, 1943</title>
          <description>
            &lt;p&gt;It’s March 2nd, 1943. The user asks your software to schedule a meeting with Acmecorp at “9 AM on the first Monday of next month”.&lt;/p&gt;&lt;pre&gt;
&lt;code&gt;
[6:17:45] homura ~ $ cal -3 2 March 1943
    February 1943          March 1943            April 1943
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6      1 &lt;span style=&quot;background: black; color: white&quot;&gt; 2&lt;/span&gt; 3  4  5  6               1  2  3
 7  8  9 10 11 12 13   7  8  9 10 11 12 13   4 &lt;span style=&quot;background: #666; color: white&quot;&gt; 5&lt;/span&gt;  6  7  8  9 10
14 15 16 17 18 19 20  14 15 16 17 18 19 20  11 12 13 14 15 16 17
21 22 23 24 25 26 27  21 22 23 24 25 26 27  18 19 20 21 22 23 24
28                    28 29 30 31           25 26 27 28 29 30
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Right now, California is on Pacific Standard Time (PST) and Arizona is on Mountain Standard Time (MST). On March 8th, California will transition to Pacific Daylight Time (PDT), one hour ahead. Arizona does not observe DST, so they’ll stay behind.&lt;/p&gt;&lt;p&gt;At least until April 1st — when the governor will sign an emergency order moving the state to MDT, effective immediately.&lt;/p&gt;&lt;p&gt;Back on March 2nd, you send an email to each participant telling them about the meeting. One of them has their locale set to en_GB, so some of the participants need to be sent “04/05/43” and some “05/04/43”.&lt;/p&gt;&lt;p&gt;A moment later, the user asks you to tell it the number of hours betweeen now and the meeting they just scheduled. The subject of the meeting is purchasing fuel for a machine that the user is now filling with enough fuel to last until then.&lt;/p&gt;&lt;p&gt;On the day of the meeting, the user drives to the Navajo reservation to conduct some unrelated business, and has to attend the meeting by phone. The reservation has been on daylight savings time since March 8th, by the way, they never stayed behind with the rest of Arizona. The user expects the software to warn them 1 hour prior to the meeting start. The border of the reservation is defined by a river, which is slowly moving East.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://mm.icann.org/pipermail/tz-announce/&quot; target=&quot;_blank&quot;&gt;The changelog for the IANA zoneinfo database&lt;/a&gt; is great, by the way, you should read it. &lt;a href=&quot;https://mm.icann.org/mailman/listinfo/tz-announce&quot; target=&quot;_blank&quot;&gt;Or subscribe&lt;/a&gt; to get it periodically&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; material delivered to your inbox!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/March-2nd-1943/</link>
        
        <pubDate>Tue, 14 Jul 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/March-2nd-1943/</guid>
      </item>
    
      <item>
        
        
          <title>General-purpose OS, special-purpose OS, and now: vendor-purpose OS</title>
          <description>
            &lt;p&gt;There have, historically, been two kinds of operating systems: general-purpose, and special-purpose. These roles are defined by the function they serve for the user. Examples of general-purpose operating systems include Unix (Linux, BSD, etc), Solaris, Haiku, Plan 9, and so on. These are well-suited to general computing tasks, and are optimized to solve the most problems possible, perhaps at the expense of those in some niche domains. Special-purpose operating systems serve those niche domains, and are less suitable for general computing. Examples of these include FreeRTOS, Rockbox, Genode, and so on.&lt;/p&gt;&lt;p&gt;These terms distinguish operating systems by the problems they solve for the user. However, a disturbing trend is emerging in which the user is not the party whose problems are being solved, and perhaps this calls for a new term. I propose “vendor-purpose operating system”.&lt;/p&gt;&lt;p&gt;I would use this term to describe Windows, macOS, Android, and iOS, and perhaps some others besides. Arguably, the first two used to be general purpose operating systems, and the latter two were once special-purpose operating systems.  Increasingly, these operating systems are making design decisions which benefit the vendor &lt;em&gt;at the expense&lt;/em&gt; of the user. For example: Windows has ads and excessive spyware, prevents you from making a local login without a Microsoft account, and aggressively pushes you to switch to Edge from other web browsers, as well as many other examples besides.&lt;/p&gt;&lt;p&gt;Apple is more subtle from the end-user’s perspective. They eschew standards to build walled gardens, opting for Metal rather than Vulkan, for example. They use cryptographic signatures to enforce a racket against developers who just want to ship their programs. They bully vendors in the app store into adding things like microtransactions to increase their revenue. They’ve also long been making similar moves in their hardware design, adding anti-features which are explicitly designed to increase their profit — adding false costs which are ultimately passed onto the consumer.&lt;/p&gt;&lt;p&gt;All of these decisions are making the OS worse for users in order to provide more value to the vendor. The operating system is becoming &lt;em&gt;less&lt;/em&gt; suited to its general-purpose tasks, as the vendor-purpose anti-features deliberately get in the way. They also become less suited at special-purpose tasks for the same reasons. These changes &lt;em&gt;are&lt;/em&gt; making improvements for one purpose: the vendor’s purpose. Therefore, I am going to start refering to these operating systems as “vendor purpose”, generally alongside a curse and a raising of the middle finger.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Vendor-purpose-OS/</link>
        
        <pubDate>Fri, 26 Jun 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Vendor-purpose-OS/</guid>
      </item>
    
      <item>
        
        
          <title>Introducing the BARE message encoding</title>
          <description>
            &lt;p&gt;I like stateless tokens. We started with state&lt;em&gt;ful&lt;/em&gt; tokens: where a generated string acts as a unique identifier for a resource, and the resource itself is looked up separately. For example, your sr.ht OAuth token is a stateful token: we just generate a random number and hand it to you, something like “a97c4aeeec705f81539aa”. To find the information associated with this token, we query the database — our local &lt;em&gt;state&lt;/em&gt; — to find it.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;#announcement&quot;&gt;Click here to skip the context and read the actual announcement -&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;But, increasingly, we’ve been using stateless tokens, which are a bloody good idea. The idea is that, instead of using random numbers, you encode the actual state you need into the token. For example, your sr.ht login session cookie is a JSON blob which is encrypted and base64 encoded. Rather than associating your session with a record in the database, we just decrypt the cookie when your browser sends it to us, and the session information is right there. This improves performance and simplicity in a single stroke, which is a huge win in my book.&lt;/p&gt;&lt;p&gt;There is one big problem, though: stateless tokens tend to be a lot larger than their stateful counterparts. For a stateful token, we just need to generate enough random numbers to be both unique and unpredictable, and then store the rest of the data elsewhere. Not so for a stateless token, whose length is a function of the amount of state which has been sequestered into it. Here’s an example: the cursor fields on the new GraphQL APIs are stateless. This is one of them:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;gAAAAABe7-ysKcvmyavwKIT9k1uVLx_GXI6OunjFIHa3OJmK3eBC9NT6507PBr1WbuGtjlZSTYLYvicH2EvJXI1eAejR4kuNExpwoQsogkE9Ua6JhN10KKYzF9kJKW0hA_-737NurotB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A whopping 141 characters long! It’s hardly as convenient to lug this monster around. Most of the time it’ll be programs doing the carrying, but it’s still annoying when you’re messing with the API and debugging your programs. This isn’t an isolated example, either: these stateless tokens tend to be large throughout sr.ht.&lt;/p&gt;&lt;p&gt;In general, JSON messages are pretty bulky. They represent everything as text, which can be 2x as inefficient for certain kinds of data right off the bat. They’re also self-describing: the schema of the message is encoded into the message itself; that is, the names of fields, hierarchy of objects, and data types.&lt;/p&gt;&lt;p&gt;There are many alternatives that attempt to address this problem, and I considered many of them. Here were a selected few of my conclusions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developers.google.com/protocol-buffers/&quot; target=&quot;_blank&quot;&gt;protobuf&lt;/a&gt;: too complicated and too fragile, and I’ve never been fond of the generated code for protobufs in any language. Writing a third-party protobuf implementation would be a gargantuan task, and there’s no standard. RPC support is also undesirable for this use-case.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://capnproto.org/&quot; target=&quot;_blank&quot;&gt;Cap’n Proto&lt;/a&gt;: fixed width, alignment, and so on — good for performance, bad for message size. Too complex. RPC support is also undesirable for this use-case. I also passionately hate C++ and I cannot in good faith consider something which makes it their primary target.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://bsonspec.org/&quot; target=&quot;_blank&quot;&gt;BSON&lt;/a&gt;: MonogoDB implementation details have leaked into the specification, and it’s extensible in the worst way. I appreciate that JSON is a closed spec and no one is making vendor extensions for it — and, similarly, a diverse extension ecosystem is not something I want to see for this technology. Additionally, encoding schema into the message is wasting space.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://msgpack.org/&quot; target=&quot;_blank&quot;&gt;MessagePack&lt;/a&gt;: ruled out for similar reasons: too much extensibility, and the schema is encoded into the message, wasting space.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://cbor.io/&quot; target=&quot;_blank&quot;&gt;CBOR&lt;/a&gt;: ruled out for similar reasons: too much extensibility, and the schema is encoded into the message. Has the advantage of a specification, but the disadvantage of that spec being 54 pages long.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There were others, but hopefully this should give you an idea of what I was thinking about when evaluating my options.&lt;/p&gt;&lt;p&gt;There doesn’t seem to be anything which meets my criteria just right:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Optimized for small messages&lt;/li&gt;&lt;li&gt;Standardized&lt;/li&gt;&lt;li&gt;Easy to implement&lt;/li&gt;&lt;li&gt;Universal — little to no support for extensions&lt;/li&gt;&lt;li&gt;Simple — no extra junk that isn’t contributing to the core mission&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The solution is evident.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/927&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/standards.png&quot;&gt;
&lt;figcaption&gt;xkcd comic 927, “Standards”&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;div id=&quot;announcement&quot;&gt;&lt;h2&gt;BARE: Binary Application Record Encoding&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://baremessages.org&quot; target=&quot;_blank&quot;&gt;BARE&lt;/a&gt; meets all of the criteria:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Optimized for small messages&lt;/strong&gt;: messages are binary, not self-describing, and have no alignment or padding.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Standardized &amp; simple&lt;/strong&gt;: the specification is just over 1,000 words — shorter than this blog post.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Easy to implement&lt;/strong&gt;: the first implementation (for Go) was done in a single weekend (this weekend, in fact).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Universal&lt;/strong&gt;: there is room for user extensibility, but it’s done in a manner which does not require expanding the implementation nor making messages which are incompatible with other implementations.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Stateless tokens aren’t the only messages that I’ve wanted a simple binary encoding for. On many occasions I’ve evaluated and re-evaluated the same set of existing solutions, and found none of them quite right. I hope that BARE will help me solve many of these problems in the future, and I hope you find it useful, too!&lt;/p&gt;&lt;p&gt;The cursor token I shared earlier in the article looks like this when encoded with BARE:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;gAAAAABe7_K9PeskT6xtLDh_a3JGQa_DV5bkXzKm81gCYqNRV4FLJlVvG3puusCGAwQUrKFLO-4LJc39GBFPZomJhkyqrowsUw==
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;100 characters (41 fewer than JSON), which happens to be the minimum size of a padded &lt;a href=&quot;https://github.com/fernet/spec/&quot; target=&quot;_blank&quot;&gt;Fernet&lt;/a&gt; message. If we compare only the cleartext:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;JSON: eyJjb3VudCI6MjUsIm5leHQiOiIxMjM0NSIsInNlYXJjaCI6bnVsbH0=
BARE: EAUxMjM0NQA=
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Much improved!&lt;/p&gt;&lt;p&gt;BARE also has an optional schema language for defining your message structure. Here’s a sample:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;type PublicKey data&amp;lt;128&amp;gt;
type Time string # ISO 8601

enum Department {
  ACCOUNTING
  ADMINISTRATION
  CUSTOMER_SERVICE
  DEVELOPMENT

  # Reserved for the CEO
  JSMITH = 99
}

type Customer {
  name: string
  email: string
  address: Address
  orders: []{
    orderId: i64
    quantity: i32
  }
  metadata: map[string]data
}

type Employee {
  name: string
  email: string
  address: Address
  department: Department
  hireDate: Time
  publicKey: optional
  metadata: map[string]data
}

type Person (Customer | Employee)

type Address {
  address: [4]string
  city: string
  state: string
  country: string
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can feed this into a code generator and get types which can encode &amp; decode these messages. But, you can also describe your schema just using your language’s existing type system, like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;go&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;Coordinates&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; {
    &lt;span class=&quot;property&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;uint&lt;/span&gt;  &lt;span class=&quot;comment&quot;&gt;// uint&lt;/span&gt;
    &lt;span class=&quot;property&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;uint&lt;/span&gt;  &lt;span class=&quot;comment&quot;&gt;// uint&lt;/span&gt;
    &lt;span class=&quot;property&quot;&gt;Z&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;uint&lt;/span&gt;  &lt;span class=&quot;comment&quot;&gt;// uint&lt;/span&gt;
    &lt;span class=&quot;property&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;uint&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// optional&amp;lt;uint&amp;gt;&lt;/span&gt;
}

&lt;span class=&quot;keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;variable function&quot;&gt;main&lt;/span&gt;() {
    &lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;coords&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;Coordinates&lt;/span&gt;
    &lt;span class=&quot;variable&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; []&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;{&lt;span class=&quot;number&quot;&gt;0x01&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0x02&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0x03&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0x01&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0x04&lt;/span&gt;}
    &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;bare&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Unmarshal&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;payload&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;coords&lt;/span&gt;)
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
        &lt;span class=&quot;function_builtin variable function&quot;&gt;panic&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt;)
    }
    &lt;span class=&quot;variable&quot;&gt;fmt&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;coords: %d, %d, %d (%d)&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;, &lt;span class=&quot;comment&quot;&gt;/* coords: 1, 2, 3 (4) */&lt;/span&gt;
        &lt;span class=&quot;variable&quot;&gt;coords&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;X&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;coords&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;Y&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;coords&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;Z&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;coords&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;Q&lt;/span&gt;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bonus: you can get the schema language definition for this struct with &lt;code&gt;schema.SchemaFor(coords)&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;BARE is under development&lt;/h2&gt;&lt;p&gt;There are some possible changes that could come to BARE before finalizing the specification. Here are some questions I’m thinking about:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Should the schema language include support for arbitrary annotations to inform code generators? I’m inclined to think “no”, but if you use BARE and find yourself wishing for this, tell me about it.&lt;/li&gt;&lt;li&gt;Should BARE have first-class support for bitfield enums?&lt;/li&gt;&lt;li&gt;Should maps be ordered?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;a href=&quot;mailto:~sircmpwn/public-inbox@lists.sr.ht&quot; target=&quot;_blank&quot;&gt;Feedback welcome&lt;/a&gt;!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Errata&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;This article was originally based on an older version of the draft specification, and was updated accordingly.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/BARE-message-encoding/</link>
        
        <pubDate>Sun, 21 Jun 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/BARE-message-encoding/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, June 2020</title>
          <description>
            &lt;p&gt;Like last month, I am writing to you from the past, preparing this status update a day earlier than usual. This time it’s because I expect to be busy with planned sr.ht maintenance tomorrow, so I’m getting the status updates written ahead of time.&lt;/p&gt;&lt;p&gt;aerc has seen lots of patches merged recently thanks to the hard work of co-maintainer Reto Brunner and the many contributors who sent patches, ranging from a scrollable folder list to improvements and bugfixes for PGP support. We wrapped all of this up in the aerc 0.4.0 release in late May. Thanks to Reto and all of the other contributors for their hard work on aerc!&lt;/p&gt;&lt;p&gt;Wayland improvements have also continued at a good pace. I’ve mentioned before that wlroots is a crucial core component tying together a lot of different parts of the ecosystem — DRM/KMS, GBM, OpenGL, libinput, udev, and more — bringing together integrations for many disparate systems and providing a single unified multiplexer for them over the Wayland protocol. Taking full advantage of all of these systems and becoming a more perfect integration of them is a long-term goal, and we’ve been continuing to make headway on these goals over the past few weeks. We are working hard to squeeze every drop of performance out of your system.&lt;/p&gt;&lt;p&gt;In the SourceHut world, I’ve been working mainly on GraphQL support, as well as Alpine 3.12 upgrades (the latter being the source of the planned outage). I wrote in some detail &lt;a href=&quot;https://sourcehut.org/blog/2020-06-10-how-graphql-will-shape-the-alpha/&quot; target=&quot;_blank&quot;&gt;on the sourcehut.org blog&lt;/a&gt; about why and how the GraphQL backends are being implemented, if you’re curious. The main development improvements in this respect which have occured since the last status updates are the introduction of a JavaScript-free GraphQL playground, and a GraphQL API for meta.sr.ht. Coming improvements will include an overhaul to authentication and OAuth2 support, and a dramatically improved approach to webhooks. Stay tuned!&lt;/p&gt;&lt;p&gt;That’s all for the time being. Thank you for your support and attention, and stay safe out there. I’ll see you next month!&lt;/p&gt;&lt;details&gt;
&lt;summary&gt;...&lt;/summary&gt;
&lt;pre&gt;
$ cat strconv/itos.$redacted
use bytes;
use types;

/***
 * Converts an i64 to a string, in base 10. The return value is statically
 * allocated and will be overwritten on subsequent calls; see [strings::dup] to
 * duplicate the result, or [strconv::itosb] to pass your own string buffer.
 *
 *	let a = strconv::i64tos(1234);
 *	io::printf(&quot;%s&quot;, a); // 1234
 *
 *	let a = strconv::i64tos(1234);
 *	let b = strconv::i64tos(4321);
 *	io::printf(&quot;%s %s&quot;, a, b); // 4321 4321
 */
export fn i64tos(i: i64) const *str =
{
	static assert(types::I64_MAX == 9223372036854775807,
		&quot;Maximum integer value exceeds buffer length&quot;);
	static let s = struct {
		l: size = 0,
		b: [22]u8 = [0: u8...], /* 20 digits plus NUL and &apos;-&apos; */
	};
	s.l = 0;
	s.b = [0: u8...];

	const isneg = i &amp;lt; 0;
	if (isneg) {
		s.b[s.l] = &apos;-&apos;: u8;
		s.l += 1;
		i = -i;
	} else if (i == 0) {
		s.b[s.l] = &apos;0&apos;: u8;
		s.l += 1;
	};

	while (i &gt; 0) {
		s.b[s.l] = &apos;0&apos;: u8 + (i % 10): u8;
		s.l += 1;
		i /= 10;
	};

	const x: size = if (isneg) 1 else 0;
	bytes::reverse(s.b[x..s.l]);

	s.b[s.l] = 0: u8;
	return &amp;s: *str;
};
&lt;/pre&gt;
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-June-2020/</link>
        
        <pubDate>Mon, 15 Jun 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-June-2020/</guid>
      </item>
    
      <item>
        
        
          <title>Can we talk about client-side certificates?</title>
          <description>
            &lt;p&gt;I’m working on improving the means by which API users authenticate with the SourceHut API. Today, I was reading &lt;a href=&quot;https://tools.ietf.org/html/rfc6749&quot; target=&quot;_blank&quot;&gt;RFC 6749&lt;/a&gt; (OAuth2) for this purpose, and it got me thinking about the original OAuth spec. I recalled vaguely that it had the API clients actually &lt;em&gt;sign&lt;/em&gt; every request, and… &lt;a href=&quot;https://tools.ietf.org/html/rfc5849&quot; target=&quot;_blank&quot;&gt;yep, indeed it does&lt;/a&gt;. This also got me thinking: what else signs requests? TLS!&lt;/p&gt;&lt;p&gt;OAuth is very complicated. The RFC is 76 pages long, the separate bearer token RFC (6750) is another 18, and no one has ever read either of them. Add JSON Web Tokens (RFC 7519, 30 pages), too. The process is complicated and everyone implements it themselves — a sure way to make mistakes in a security-critical component. Not all of the data is authenticated, no cryptography is involved at any step, and it’s easy for either party to end up in an unexpected state. The server has to deal with problems of revocation and generating a secure token itself. Have you ever met anyone who feels positively about OAuth?&lt;/p&gt;&lt;p&gt;Now, take a seat. Have a cup of coffee. I want to talk about client-side certificates. Why didn’t they take off? Let’s sketch up a hypothetical TLS-based protocol as an alternative to OAuth.  Picture the following…&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You, an API client developer, generate a certificate authority and intermediate, and you upload your CA certificate to the Service Provider as part of your registration as a user agent.&lt;/li&gt;&lt;li&gt;When you want a user to authorize you to access their account, you generate a certificate for them, and redirect them to the Service Provider’s authorization page with a &lt;abbr title=&quot;Certificate Signing Request&quot;&gt;CSR&lt;/abbr&gt; in tow. Your certificate includes, among other things, the list of authorized scopes for which you want to be granted access. It is already signed with your client CA key, or one of its intermediates.&lt;/li&gt;&lt;li&gt;The client reviews the desired access, and consents. They are redirected back to your API client application, along with the signed certificate.&lt;/li&gt;&lt;li&gt;Use this client-side certificate to authenticate your API requests. Hooray!&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Several advantages to this approach occur to me.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You get strong encryption and authentication guarantees for free.&lt;/li&gt;&lt;li&gt;TLS is basically the single most ironclad, battle-tested security mechanism on the internet, and mature implementations are available for every platform. Everyone implements OAuth themselves, and often poorly.&lt;/li&gt;&lt;li&gt;Client-side certificates are stateless. They contain all of the information necessary to prove that the client is entitled to access.&lt;/li&gt;&lt;li&gt;If you handle SSL termination with nginx, haproxy, etc, you can reject unauthorized requests before your application backend ever even sees them.&lt;/li&gt;&lt;li&gt;The service provider can untrust the client’s CA in a single revocation, if they are malicious or lose their private keys.&lt;/li&gt;&lt;li&gt;The API client and service provider are both always certain that the process was deliberately initiated by the API client. No weird state tokens to carry through the process like OAuth uses!&lt;/li&gt;&lt;li&gt;Lots of free features: any metadata you like, built-in expirations, API clients can self-organize into intermediates at their discretion, and so on.&lt;/li&gt;&lt;li&gt;Security-concious end users can toggle a flag in their account which would, as part of the consent process, ask them to sign the API client’s certificate themselves, before the signed certificate is returned to the API client. Then any API request authorized for that user’s account has to be signed by the API client, the service provider, &lt;em&gt;and&lt;/em&gt; the user to be valid.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Here’s another example: say your organization has several services, each of which interacts with a subset of Acme Co’s API on behalf of their users. Your organization generates a single root CA, and signs up for Acme Co’s API with it. Then you issue intermediate CAs to each of your services, which are &lt;em&gt;only&lt;/em&gt; allowed to issue CSRs for the subset of scopes they require. If any service is compromised, it can’t be used to get more access than it already had, and you can revoke just that one intermediate without affecting the rest.&lt;/p&gt;&lt;p&gt;Even some famous downsides, such as &lt;abbr title=&quot;Certificate Revocation
Lists&quot;&gt;CRLs&lt;/abbr&gt; and &lt;abbr title=&quot;Online Certificate Status
Protocol&quot;&gt;OCSP&lt;/abbr&gt;, are mitigated here, because the system is much more centralized. You control all of the endpoints which will be validating certificates, you can just distribute revocations directly to them as soon as they come in.&lt;/p&gt;&lt;p&gt;The advantages are clearly numerous. Let’s wrap it up in a cute, Google-able name, write some nice tooling and helper libraries for it, and ship it!&lt;/p&gt;&lt;p&gt;Or, maybe not. I have a nagging feeling that I’m missing something here. It doesn’t seem right that such an obvious solution would have been left on the table, by everyone, for decades. Maybe it’s just that the whole certificate signing dance has left a bad taste in everyone’s mouth — many of us have not-so-fond memories of futzing around with the awful OpenSSL CLI to generate a CSR. But, there’s no reason why we couldn’t do it better, and more streamlined, if we had the motivation to.&lt;/p&gt;&lt;p&gt;There are also more use-cases for client-side certificates that seem rather compelling, such as an alternative to user passwords. Web browser support for client-side certificates totally sucks, but that is a solvable problem.&lt;/p&gt;&lt;p&gt;For the record, I have no intention of using this approach for the SourceHut API. This thought simply occurred to me, and I want to hear what you think. Why aren’t we using client-side certificates?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Can-we-talk-about-client-side-certs/</link>
        
        <pubDate>Fri, 12 Jun 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Can-we-talk-about-client-side-certs/</guid>
      </item>
    
      <item>
        
        
          <title>Add a &quot;contrib&quot; directory to your projects</title>
          <description>
            &lt;p&gt;There’s a common pattern among free- and open-source software projects to include a “contrib” directory at the top of their source code tree. I’ve seen this in many projects for many years, but I’ve seen it discussed only rarely — so here we are!&lt;/p&gt;&lt;p&gt;The contrib directory is used as an unorganized (or, at best, lightly organized) bin of various useful things &lt;strong&gt;contrib&lt;/strong&gt;uted by the community around the software, but which is not necessarily a good candidate for being a proper part of the software. Things in contrib should not be wired into your build system, shouldn’t be part of your automated testing, shouldn’t be included in your documentation, and should not be installed with your packages. contrib entries are not supported by the maintainers, and are given only a light code review at the most. There is no guarantee whatsoever of workitude or maintenance for anything found in contrib.&lt;/p&gt;&lt;p&gt;Nevertheless, it is often useful to have such a place to put various little scripts, config files, and so on, which provide a helpful leg-up for users hoping to integrate the software with some third-party product, configure it to fit nicely into an unusual environment, coax it into some unusual behavior, or whatever else the case may be. The idea is to provide a place to drop a couple of files which might save a future someone facing similar problems from doing all of the work themselves. Such people can contribute back small fixes or improvements, and the maintenance burden of such contributions lies entirely with the users.&lt;/p&gt;&lt;p&gt;If the contributor wants to take on a greater maintenance burden, this kind of stuff is better suited to a standalone project, with its own issue tracking, releases, and so on. If you just wrote a little script and want somewhere to drop it so that others may find it useful, then contrib is the place for you.&lt;/p&gt;&lt;p&gt;For a quick example, let’s consult Sway’s &lt;a href=&quot;https://github.com/swaywm/sway/tree/master/contrib&quot; target=&quot;_blank&quot;&gt;contrib folder&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;_incr_version
autoname-workspaces.py
grimshot
grimshot.1
grimshot.1.scd
inactive-windows-transparency.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;_incr_version&lt;/code&gt; script is something that I use myself to help with preparing new releases. It is a tool useful only to maintainers, and therefore is not distributed with the project.&lt;/p&gt;&lt;p&gt;Looking at &lt;code&gt;autoname-workspaces.py&lt;/code&gt; next, from which we can see that the quality criteria is reduced for members of contrib — none of Sway’s upstream code is written in Python, and the introduction of such a dependency would be controversial. This script automatically changes your workspace name based on what applications you’re running in it — an interesting workflow, but quite different from the OOTB experience.&lt;/p&gt;&lt;p&gt;&lt;code&gt;grimshot&lt;/code&gt; is a shell script which ties together many third-party programs (grim, slurp, wl-copy, jq, and notify-send) to make a convenient way of taking screenshots. Adding this upstream would introduce &lt;em&gt;a lot&lt;/em&gt; of third-party dependencies for a minor convenience. This tool has had a bit more effort put into it: notice that a man page is provided as well. Because the contrib directory does not participate in the upstream build system, the contributor has also added a pre-compiled man page so that you can skip this step when installing it on your system.&lt;/p&gt;&lt;p&gt;Last, we have &lt;code&gt;inactive-windows-transparency.py&lt;/code&gt;, which is a script for making all windows other than your focused one semi-transparent. Some people may want this, but again, it’s not really something we’d consider appropriate for the OOTB experience. Perfect for contrib!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Add-a-contrib-directory/</link>
        
        <pubDate>Sat, 06 Jun 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Add-a-contrib-directory/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, May 2020</title>
          <description>
            &lt;p&gt;Hello, future readers! I am writing to you from one day in the past. I finished my plans for today early and thought I’d get a head start on writing the status updates for tomorrow, or rather, for today. From your reference frame, that is.&lt;/p&gt;&lt;p&gt;Let’s start with Wayland. First, as you might have heard, &lt;a href=&quot;https://wayland-book.com&quot; target=&quot;_blank&quot;&gt;The Wayland Protocol&lt;/a&gt; is now free for anyone to read, and has been relicensed as CC-BY-SA. Enjoy! It’s still not quite done, but most of it’s there. In development news, wlroots continues to enjoy incremental improvements, and is being refined further and further towards a perfect citizen of the ecosystem in which it resides. Sway as well has seen many small bugfixes and improvements. Both have been been stable for a while now: the only meaningful changes will be, for the most part, a steady stream of bug fixes and performance improvements.&lt;/p&gt;&lt;p&gt;Moving on from Wayland, then, there are some interesting developments in the world of email as well. aerc has seen some minor changes to how it handles templates and custom email headers, and a series of other small features and improvements: drafts, a &lt;code&gt;:choose&lt;/code&gt; meta-command, and fixes for OpenBSD and Go 1.15. Additionally, I’ve joined &lt;a href=&quot;https://emersion.fr/&quot; target=&quot;_blank&quot;&gt;Simon Ser&lt;/a&gt; to work on &lt;a href=&quot;https://sr.ht/~emersion/alps/&quot; target=&quot;_blank&quot;&gt;Alps&lt;/a&gt; together, to put the finishing touches on our lightweight &amp; customizable webmail client before &lt;a href=&quot;https://www.migadu.com/en/index.html&quot; target=&quot;_blank&quot;&gt;Migadu&lt;/a&gt; puts it into production.&lt;/p&gt;&lt;p&gt;On the SourceHut front, lots of cool stuff came out this month. You might have seen the &lt;a href=&quot;https://sourcehut.org/blog/2020-05-11-sourcehut-plus-plan-9/&quot; target=&quot;_blank&quot;&gt;announcement this week&lt;/a&gt; that we’ve added Plan 9 support to the CI — a world first :D I also just published the first bits of the new, experimental GraphQL API for git.sr.ht, which you can &lt;a href=&quot;https://git.sr.ht/graphql&quot; target=&quot;_blank&quot;&gt;play with here&lt;/a&gt;. And, of course, the long-awaited project hub was released this month! &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;Check it out here&lt;/a&gt; to get your projects listed. I’ll post about all of this in more detail on the sr.ht-announce mailing list later today.&lt;/p&gt;&lt;p&gt;That’s all for today! I’ll see you next month. Thank you once more for your wonderful support.&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;...&lt;/summary&gt;
&lt;pre&gt;/* sys::write */
fn write(fd: int, buf: *void, count: size) size;

fn puts(s: str) size =
{
	let n = write(1, s: *char, len(s));
	n += write(1, &quot;\n&quot;: *char, 1);
	n;
};

export fn main int =
{
	puts(&quot;Hello world!&quot;);
	0;
};
&lt;/pre&gt;

&lt;pre&gt;
$ ./[redacted] &amp;lt; example.[redacted] | qbe &amp;gt; example.S
$ as -o example.o example.S
$ ld -o example lib/sys/[redacted]s.o example.o lib/sys/lib[redacted]rt.a
$ wc -c example
9640
$ ./example
Hello world!
&lt;/pre&gt;
&lt;/details&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-May-2020/</link>
        
        <pubDate>Fri, 15 May 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-May-2020/</guid>
      </item>
    
      <item>
        
        
          <title>We are complicit in our employer&apos;s deeds</title>
          <description>
            &lt;p&gt;Tim Bray’s excellent “&lt;a href=&quot;https://www.tbray.org/ongoing/When/202x/2020/04/29/Leaving-Amazon&quot; target=&quot;_blank&quot;&gt;Bye Amazon&lt;/a&gt;” post inspired me to take this article off of my backlog, where it has been sitting for a few weeks. I applaud Tim for stepping down from a company that has demonstrated itself incompatible with his sense of right and wrong, and I want to take a moment to remind you that the rest of us in the tech industry have the same opportunity — no, the same &lt;em&gt;obligation&lt;/em&gt; as Tim did.&lt;/p&gt;&lt;p&gt;As software engineers, we enjoy high salaries and extremely good job security. A good software engineer with only a couple of years of experience under their belt can expect to have an offer within 1 or 2 months of starting their search. It can seem a little scary and stressful, but if you’re a programmer already working at $company and you’re looking for a change, you’re better off than 99% of your non-technical friends. In tech, hardly anyone is “trapped” at a bad job; or at least we don’t have a good excuse for not trying for something better.&lt;/p&gt;&lt;p&gt;Tim calls out Amazon’s terrible, unhealthy working conditions and retaliation against staff who speak up or try to organize.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Google conducts mass surveillance, kowtows to oppressive regimes, and punishes workers who stand up to them. Less obvious stuff, too — Apple builds walled gardens and makes targeted attacks on open standards, Facebook is a giant surveillance tool which routinely disregards the law, the same behavior which made Uber and Airbnb into the giants they are today, all while fostering a “gig” culture in which the poor have no stability or security. Mass surveillance, contempt of the law, tax evasion, oppression of the poor, of minorities… this is what our industry is known for, and it’s &lt;em&gt;our&lt;/em&gt; fault.&lt;/p&gt;&lt;p&gt;This is why I hold my peers accountable for working at companies which are making a negative impact on the world around them. As a general rule, it costs a business your salary × 1.5 to employ you, given the overhead of benefits, HR, training, and so on. When you’re making a cool half-million annual salary from $bigcorp, it’s because they expect to make at least ¾ of a million that they wouldn’t be making without you. It does not make economic sense for them to hire you if this weren’t the case. Your contribution makes a big difference.&lt;/p&gt;&lt;p&gt;If the best defense we have for working at these companies is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Superior_orders&quot; target=&quot;_blank&quot;&gt;Nuremberg defense&lt;/a&gt;, that doesn’t reflect well on us. But, maybe you would object, maybe you would have the courage to say “no” when asked to do these things. Maybe you would, but someday, a cool project will come across your inbox - machine learning!  Big data! Cloud scale! It’s everything you were promised when you took the job, and have more fun with it for a few months than you have had in a long time. Your superiors are thrilled - “it’s perfect!”, they say, and it’s not until they take it and start feeding it real-world data that &lt;a href=&quot;https://observer.com/2020/04/amazon-whole-foods-anti-union-technology-heat-map/&quot; target=&quot;_blank&quot;&gt;you realize exactly what you have built&lt;/a&gt;. Doublethink quickly steps in to protect your ego from the cognitive dissonance, and you take another little step towards becoming the person you once swore never to be.&lt;/p&gt;&lt;p&gt;The rapid computerization of society has decreased the time necessary to build novel machines one thousand-fold. This endows us with a great responsibility, because whatever we build with them, the changes they bring to society will be upon us much, much faster than any changes to come before. Every software developer possesses alone the potential of 50 engineers living just 100 years ago. We can apply this power for good or for ill, but it’s up to each of us to make a deliberate choice on the matter.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/We-are-complicit-in-our-employers-deeds/</link>
        
        <pubDate>Tue, 05 May 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/We-are-complicit-in-our-employers-deeds/</guid>
      </item>
    
      <item>
        
        
          <title>How to store data forever</title>
          <description>
            &lt;p&gt;As someone who has been often maligned by the disappearance of my data for various reasons — companies going under, hard drive failure, etc — and as someone who is responsible for the safekeeping of other people’s data, I’ve put a lot of thought into solutions for long-term data retention.&lt;/p&gt;&lt;p&gt;There are two kinds of long-term storage, with different concerns: cold storage and hot storage. The former is like a hard drive in your safe — it stores your data, but you’re not actively using it or putting wear on the storage medium. By contrast, hot storage is storage which is available immediately and undergoing frequent reads and writes.&lt;/p&gt;&lt;h2&gt;What storage medium to use?&lt;/h2&gt;&lt;p&gt;There are some bad ways to do it. The worst way I can think of is to store it on a microSD card. These fail &lt;em&gt;a lot&lt;/em&gt;. I couldn’t find any hard data, but anecdotally, 4 out of 5 microSD cards I’ve used have experienced failures resulting in permanent data loss. Low volume writes, such as from a digital camera, are unlikely to cause failure. However, microSD cards have a tendency to get hot with prolonged writes, and they’ll quickly leave their safe operating temperature and start to accumulate damage. Nearly all microSD cards will let you perform writes fast enough to drive up the temperature beyond the operating limits — after all, writes per second is a marketable feature — so if you want to safely move lots of data onto or off of a microSD card, you need to monitor the temperature and throttle your read/write operations.&lt;/p&gt;&lt;p&gt;A more reliable solution is to store the data on a hard drive&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. However, hard drives are rated for a limited number of read/write cycles, and can be expected to fail eventually. Backblaze publishes some great articles on &lt;a href=&quot;https://www.backblaze.com/blog/hard-drive-stats-for-2019/&quot; target=&quot;_blank&quot;&gt;hard drive failure rates&lt;/a&gt; across their fleet. According to them, the average annual failure rate of hard drives is almost 2%. Of course, the exact rate will vary with the frequency of use and storage conditions. Even in cold storage, the shelf life of a magnetic platter is not indefinite.&lt;/p&gt;&lt;p&gt;There are other solutions, like optical media, tape drives, or more novel mediums, like the &lt;a href=&quot;https://en.wikipedia.org/wiki/Rosetta_Project&quot; target=&quot;_blank&quot;&gt;Rosetta Disk&lt;/a&gt;. For most readers, a hard drive will be the best balance of practical and reliable. For serious long-term storage, if expense isn’t a concern, I would also recommend hot storage over cold storage because it introduces the possibility of active monitoring.&lt;/p&gt;&lt;h2&gt;Redundancy with RAID&lt;/h2&gt;&lt;p&gt;One solution to this is redundancy — storing the same data across multiple hard drives. For cold storage, this is often as simple as copying the data onto a second hard drive, like an external backup HDD. Other solutions exist for hot storage. The most common standard is &lt;a href=&quot;https://en.wikipedia.org/wiki/RAID&quot; target=&quot;_blank&quot;&gt;RAID&lt;/a&gt;, which offers different features with different numbers of hard drives. With two hard drives (RAID1), for example, it utilizes mirroring, which writes the same data to both disks. RAID gets more creative with three or more hard drives, utilizing &lt;em&gt;parity&lt;/em&gt;, which allows it to reconstruct the contents of failed hard drives from still-online drives. The basic idea relies on the XOR operation. Let’s say you write the following byte to drive A: &lt;code&gt;0b11100111&lt;/code&gt;, and to drive B: &lt;code&gt;0b10101100&lt;/code&gt;. By XORing these values together:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;  11100111 A
^ 10101100 B
= 01001011 C
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We obtain the value to write to drive C. If any of these three drives fail, we can XOR the remaining two values again to obtain the third.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;  11100111 A
^ 01001011 C
= 10101100 B

  10101100 B
^ 01001011 C
= 11100111 A
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This allows any drive to fail while still being able to recover its contents, and the recovery can be performed online. However, it’s often not that simple. Drive failure can dramatically reduce the performance of the array while it’s being rebuilt — the disks are going to be seeking constantly to find the parity data to rebuild the failed disk, and any attempts to read from the disk that’s being rebuilt will require computing the recovered value on the fly. This can be improved upon by using lots of drives and multiple levels of redundancy, but it is still likely to have an impact on the availability of your data if not carefully planned for.&lt;/p&gt;&lt;p&gt;You should also be monitoring your drives and preparing for their failure in advance.  Failing disks can show signs of it in advance — degraded performance, or via S.M.A.R.T reports. Learn the tools for monitoring your storage medium, such as smartmontools, and set it up to report failures to you (and &lt;em&gt;test&lt;/em&gt; the mechanisms by which the failures are reported to you).&lt;/p&gt;&lt;h3&gt;Other RAID failure modes&lt;/h3&gt;&lt;p&gt;There are other common ways a RAID can fail that result in permanent data loss. One example is using hardware RAID — there was an argument to be made for them at one point, but these days hardware RAID is &lt;em&gt;almost always&lt;/em&gt; a mistake. Most operating systems have software RAID implementations which can achieve the same results without a dedicated RAID card. With hardware RAID, if the RAID card itself fails (and they often do), you might have to find the exact same card to be able to read from your disks again. You’ll be paying for new hardware, which might be expensive or out of production, and waiting for it to arrive before you can start recovering data. With software RAID, the hard drives are portable between machines and you can always interpret the data with general purpose software.&lt;/p&gt;&lt;p&gt;Another common failure is &lt;em&gt;cascading&lt;/em&gt; drive failures. RAID can tolerate partial drive failure thanks to parity and mirroring, but if the failures start to pile up, you can suffer permanent data loss. Many a sad administrator has been in panic mode, recovering a RAID from a disk failure, and at their lowest moment… another disk fails. Then another. They’ve suddenly lost their data, and the challenge of recovering what remains has become ten times harder. When you’ve been distributing read and write operations consistently across all of your drives over the lifetime of the hardware, they’ve been receiving a similar level of wear, and failing together is not uncommon.&lt;/p&gt;&lt;p&gt;Often, failures like this can be attributed to using many hard drives from the same batch. One strategy I recommend to avoid this scenario is to use drives from a mix of vendors, model numbers, and so on. Using a RAID improves performance by distributing reads and writes across drives, using the time one drive is busy to utilize an alternate. Accordingly, any differences in the performance characteristics of different kinds of drives will be smoothed out in the wash.&lt;/p&gt;&lt;h2&gt;ZFS&lt;/h2&gt;&lt;p&gt;RAID is complicated, and getting it right is difficult. You don’t want to wait until your drives are failing to learn about a gap in your understanding of RAID. For this reason, I recommend ZFS to most. It automatically makes good decisions for you with respect to mirroring and parity, and gracefully handles rebuilds, sudden power loss, and other failures. It also has features which are helpful for other failure modes, like snapshots.&lt;/p&gt;&lt;p&gt;Set up Zed to email you reports from ZFS. Zed has a debug mode, which will send you emails even for working disks — I recommend leaving this on, so that their conspicuous absence might alert you to a problem with the monitoring mechanism. Set up a cronjob to do monthly scrubs and review the Zed reports when they arrive. ZFS snapshots are cheap - set up a cronjob to take one every 5 minutes, perhaps with &lt;a href=&quot;https://github.com/zfsonlinux/zfs-auto-snapshot&quot; target=&quot;_blank&quot;&gt;zfs-auto-snapshot&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Human failures and existential threats&lt;/h2&gt;&lt;p&gt;Even if you’ve addressed hardware failure, you’re not done yet. There are other ways still in which your storage may fail. Maybe your server fans fail and burn out all of your hard drives at once. Or, your datacenter could suffer a total existence failure — what if a fire burns down the building?&lt;/p&gt;&lt;p&gt;There’s also the problem of human failure. What if you accidentally &lt;code&gt;rm -rf / *&lt;/code&gt; the server? Your RAID array will faithfully remove the data from all of the hard drives for you. What if you send the sysop out to the datacenter to decommission a machine, and no one notices that they decommissioned the wrong one until it’s too late?&lt;/p&gt;&lt;p&gt;This is where off-site backups come into play. For this purpose, I recommend &lt;a href=&quot;https://www.borgbackup.org/&quot; target=&quot;_blank&quot;&gt;Borg backup&lt;/a&gt;. It has sophisticated features for compression and encryption, and allows you to mount any version of your backups as a filesystem to recover the data from. Set this up on a cronjob as well for as frequently as you feel the need to make backups, and send them off-site to another location, which itself should have storage facilities following the rest of the recommendations from this article. Set up another cronjob to run &lt;code&gt;borg check&lt;/code&gt; and send you the results on a schedule, so that their conspicuous absence may indicate that something fishy is going on. I also use &lt;a href=&quot;https://prometheus.io/&quot; target=&quot;_blank&quot;&gt;Prometheus&lt;/a&gt; with &lt;a href=&quot;https://github.com/prometheus/pushgateway&quot; target=&quot;_blank&quot;&gt;Pushgateway&lt;/a&gt; to make a note every time that a backup is run, and set up an alarm which goes off if the backup age exceeds 48 hours. I also have periodic test alarms, so that the alert manager’s own failures are noticed.&lt;/p&gt;&lt;h2&gt;Are you prepared for the failure?&lt;/h2&gt;&lt;p&gt;When your disks are failing and everything is on fire and the sky is falling, this is the worst time to be your first rodeo. You should have &lt;em&gt;practiced&lt;/em&gt; these problems before they became problems. Do training with anyone expected to deal with failures. Yank out a hard drive and tell them to fix it. Have someone in sales come yell at them partway through because the website is unbearably slow while the RAID is rebuilding and the company is losing $100 per minute as a result of the outage.&lt;/p&gt;&lt;p&gt;Periodically produce a working system from your backups. This proves (1) the backups are still working, (2) the backups have coverage over everything which would need to be restored, and (3) you know how to restore them. Bonus: if you’re confident in your backups, you should be able to replace the production system with the restored one and allow service to continue as normal.&lt;/p&gt;&lt;h2&gt;Actually storing data &lt;em&gt;forever&lt;/em&gt;&lt;/h2&gt;&lt;p&gt;Let’s say you’ve managed to keep your data around. But will you still know how to interpret that data in the future? Is it in a file format which requires specialized software to use? Will that software still be relevant in the future? Is that software open-source, so you can update it yourself? Will it still compile and run correctly on newer operating systems and hardware? Will the storage medium still be compatible with new computers?&lt;/p&gt;&lt;p&gt;Who is going to be around to watch the monitoring systems you’ve put in place? Who’s going to replace the failing hard drives after you’re gone? How will they be paid? Will the dataset still be comprehensible after 500 years of evolution of written language? The dataset requires constant maintenance to remain intact, but also to remain useful.&lt;/p&gt;&lt;p&gt;And ultimately, there is one factor to long-term data retention that you cannot control: future generations will decide what data is worth keeping — not us.&lt;/p&gt;&lt;p&gt;In summary: no matter what, definitely don’t do this:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/802d6207.jpg&quot;&gt;
&lt;figcaption&gt;Picture of a SATA card for RAIDing 10 microSD cards together&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-store-data-forever/</link>
        
        <pubDate>Wed, 22 Apr 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-store-data-forever/</guid>
      </item>
    
      <item>
        
        
          <title>Configuring aerc for git via email</title>
          <description>
            &lt;p&gt;I use &lt;a href=&quot;https://aerc-mail.org&quot; target=&quot;_blank&quot;&gt;aerc&lt;/a&gt; as my email client (naturally — I wrote it, after all), and I use &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;git send-email&lt;/a&gt; to receive patches to many of my projects. I designed aerc specifically to be productive for this workflow, but there are a few extra things that I use in my personal aerc configuration that I thought were worth sharing briefly. This blog post will be boring and clerical, feel free to skip it unless it’s something you’re interested in.&lt;/p&gt;&lt;p&gt;When I want to review a patch, I first tell aerc to &lt;code&gt;:cd sources/&amp;lt;that project&amp;gt;&lt;/code&gt;, then I open up the patch and give it a read. If it needs work, I’ll use “rq” (“reply quoted”), a keybinding which is available by default, to open my editor with the patch pre-quoted to trim down and reply with feedback inline. If it looks good, I use the first of my custom keybindings: “ga”, short for git am. The entry in &lt;code&gt;~/.config/aerc/binds.conf&lt;/code&gt; is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ga = :pipe -mb git am -3&amp;lt;Enter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This pipes the entire message (-m, in case I’m viewing a message part) into &lt;code&gt;git am -3&lt;/code&gt; (-3 uses a three-way merge, in case of conflicts), in the background (-b). Then I’ll use C-t (ctrl-T), another keybinding which is included by default, to open a terminal tab in that directory, where I can compile the code, run the tests, and so on. When I’m done, I use the “gp” keybinding to push the changes:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;gp = :term git push&amp;lt;Enter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This runs the command in a new terminal, so I can monitor the progress. Finally, I like to reply to the patch, letting the contributor know their work was merged and thanking them for the contribution. I have a keybinding for this, too:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;rt = :reply -Tthanks&amp;lt;Enter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;My “thanks” template is at &lt;code&gt;~/.config/aerc/templates/thanks&lt;/code&gt; and looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Thanks!

{% raw %}
{{exec &amp;quot;{ git remote get-url --push origin;
    git reflog -2 origin/master --pretty=format:%h; }
  | xargs printf &amp;apos;To %s\n   %s..%s  master -&amp;gt; master\n&amp;apos;&amp;quot; &amp;quot;&amp;quot;}}
{% endraw %}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That git command prints a summary of the most recent push to master. The result is that my editor is pre-filled with something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Thanks!

To git@git.sr.ht:~sircmpwn/builds.sr.ht
   7aabe74..191f4a0  master -&amp;gt; master
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I occasionally append a few lines asking questions about follow-up work or clarifying the deployment schedule for the change.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Configuring-aerc-for-git/</link>
        
        <pubDate>Mon, 20 Apr 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Configuring-aerc-for-git/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, April 2020</title>
          <description>
            &lt;p&gt;Wow, it’s already time for another status update? I’m starting to lose track of the days stuck inside. I have it easier than many - I was already used to working from home before any of this began. But, weeks and weeks of not spending IRL time with anyone else is starting to get to me. Remember to call your friends and family and let them know how you’re doing. Meanwhile, I’ve had a productive month - let’s get you up to date!&lt;/p&gt;&lt;p&gt;In the Wayland world, I’ve made some more progress on the book. The input chapter is now finished, including the example code. The main things which remain to be written are the XDG positioner section (which I am dreading), drag and drop, and protocol extensions. On the code side of things, wlroots continues to see gradual improvements — the DRM (not the bad kind) implementation continues to see improvements, expanding to more and more use-cases with even better performance. Sway has also seen little bug fixes here and there, and updates to keep up with wlroots.&lt;/p&gt;&lt;p&gt;For my part, I’ve mostly been focused on SourceHut and Secret Project this month. On the SourceHut side of things, I’ve been working on hub.sr.ht, and on an experimental GraphQL-based API for git.sr.ht. The former is progressing quite well, and I hope to ship an early version before the next status update. As for the latter, it’s still very experimental, but I am optimistic about it. I have felt that the current REST API design was less than ideal, and the best time to change it would be during the alpha. The GraphQL design, while it has its limitations, is a lot better than the REST design and should make it a lot easier for services to interop with each other - which is a core design need for sr.ht.&lt;/p&gt;&lt;p&gt;Here’s a little demo of hub.sr.ht as of a few weeks ago to whet your appetite:&lt;/p&gt;&lt;video src=&quot;https://yukari.sr.ht/hub.sr.ht.webm&quot; muted autoplay loop controls&gt;
  Your web browser does not support the webm video codec. Please consider using
  web browsers that support free and open standards.
&lt;/video&gt;
&lt;p&gt;As far as the secret project is concerned, here’s another teaser:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;fn printf (fmt: str, ...) int;

fn f (ptr: &amp;amp;int) int =
{
	let x: int = *ptr;
	free ptr;
	printf(&amp;quot;value: %d\n&amp;quot;, x)
};

export fn main int =
{
	let x = alloc &amp;amp;int 10;
	f(^x);
	0
};
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;$ [redacted] -o example [redacted...]
$ ./example
value: 10
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That’s all for today! I’ll see you again next month. Thank you for your support!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-April-2020/</link>
        
        <pubDate>Wed, 15 Apr 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-April-2020/</guid>
      </item>
    
      <item>
        
        
          <title>My unorthodox, branchless git workflow</title>
          <description>
            &lt;p&gt;I have been using git for a while, and I took the time to learn about it in great detail. Equipped with an understanding of its internals and a comfortable familiarity with tools like &lt;a href=&quot;https://git-rebase.io&quot; target=&quot;_blank&quot;&gt;git rebase&lt;/a&gt; — and a personal, intrinsic desire to strive for minimal and lightweight solutions — I have organically developed a workflow which is, admittedly, somewhat unorthodox.&lt;/p&gt;&lt;p&gt;In short, I use git branches very rarely, preferring to work on my local master branch almost every time. When I want to work on multiple tasks in the same repository (i.e. often), I just… work on all of them on master. I waste no time creating a new branch, or switching to another branch to change contexts; I just start writing code and committing changes, all directly on master, intermixing different workstreams freely.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; This reduces my startup time to zero, both for starting new tasks and revisiting old work.&lt;/p&gt;&lt;p&gt;When I’m ready to present some or all of my changes to upstream, I grab git rebase and reorganize all of these into their respective features, bugfixes, and so on, forming a series of carefully organized, self-contained patchsets. When I receive feedback, I just start correcting the code right away, then fixup the old commits during the rebase. Often, I’ll bring the particular patchset I’m ready to present upstream to the front of my master branch at the same time, for convenient access with &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;git send-email&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I generally set my local master branch to track the remote master branch,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; so I can update my branch with &lt;code&gt;git pull --rebase&lt;/code&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Because all of my work-in-progress features are on the master branch, this allows me to quickly address any merge conflicts with upstream for &lt;em&gt;all&lt;/em&gt; of my ongoing work at once. Additionally, by keeping them all on the same branch, I can be assured that my patches are mutually applicable and that there won’t be any surprise conflicts in feature B after feature A is merged upstream.&lt;/p&gt;&lt;p&gt;If I’m working on my own projects (where I can push to upstream master), I’ll still be working on master. If I end up with a few commits queued up and I need to review some incoming patches, I’ll just apply them to master, rebase them behind my WIP work, and then use &lt;code&gt;git push origin HEAD~5:refs/heads/master&lt;/code&gt; to send them upstream, or something to that effect.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; Bonus: this instantly rebases my WIP work on top of the new master branch.&lt;/p&gt;&lt;p&gt;This workflow saves me time in several ways:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;No time spent creating new branches for new features.&lt;/li&gt;&lt;li&gt;No time spent switching between branches to address feedback.&lt;/li&gt;&lt;li&gt;All of my features are guaranteed to be mutually applicable to master, saving me time addressing conflicts.&lt;/li&gt;&lt;li&gt;Any conflicts with upstream are addressed in all of my workstreams at once, without switching between branches or allowing any branch to get stale.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I know that lightweight branches are one of git’s flagship features, but I don’t really use them. I know it’s weird, sue me.&lt;/p&gt;&lt;p&gt;Sometimes I do use branches, though, when I know that a workstream is going to be a lot of work — it involves lots of large-scale refactoring, or will take several weeks to complete. This isolates it from my normal workflow on small-to-medium patches, acknowledging that the large workstream is going to be more prone to conflicts. By addressing these separately, I don’t waste my time fixing up the error-prone branch all the time while I’m working on my smaller workstreams.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/My-weird-branchless-git-workflow/</link>
        
        <pubDate>Mon, 06 Apr 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/My-weird-branchless-git-workflow/</guid>
      </item>
    
      <item>
        
        
          <title>Designing and 3D printing a new part for my truck</title>
          <description>
            &lt;p&gt;I drove a car daily for many years while I was living in Colorado, California, and New Jersey, but since I moved to Philadelphia I have not needed a car. The public transit here is not great, but it’s good enough to get where I need to be and it’s a lot easier than worrying about parking a car. However, in the past couple of years, I have been moving more and more large server parts back and forth to the datacenter for SourceHut. I’ve also developed an interest in astronomy, which benefits from being able to carry large equipment to remote places. These reasons, among others, put me into the market for a vehicle once again.&lt;/p&gt;&lt;p&gt;I think of a vehicle strictly as a functional tool. Some creature comforts are nice, but I consider them optional. Instead, I prioritize utility. A truck makes a lot of sense — lots of room to carry things in. And, given my expected driving schedule of “not often”, I wasn’t looking to spend a lot of money or get a loan. There are other concerns: modern cars are very complicated machines, and many have lots of proprietary computerized components which make end-user maintenance very difficult. Sometimes manufacturers even use cryptography and legal threats to bring cars into their dealerships, bullying out third-party repairs.&lt;/p&gt;&lt;p&gt;To avoid these, I got an older truck: a 1984 Dodge D250. It’s a much simpler machine than most modern cars, and learning how to repair and maintain it is something I can do in my spare time.&lt;/p&gt;&lt;p&gt;It’s an old truck, and the previous owners were not negligent, but also didn’t invest a lot of time or money in the vehicle’s upkeep. The first problem I hit was the turn signal lever snapping and becoming slack, which I fixed by pulling open the steering column, re-aligning the lever, and tightening an internal screw. The more interesting problem, however, was this:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/d798184b.jpg&quot;&gt;
&lt;figcaption&gt;Picture of a broken latch on the window over the truck bay&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;This plastic part holds an arm in place, which is engaged by a lever in the center of the window which folds closed over the truck bay. It’s used to hold the window in place and provides a weak locking mechanism. When the arm is allowed to move freely, it can clang around while I’m driving, and can make opening the truck bay a frustrating procedure. I have been looking for a reason to learn how to use &lt;a href=&quot;http://solvespace.com/index.pl&quot; target=&quot;_blank&quot;&gt;solvespace&lt;/a&gt;, and this seemed like a great start.&lt;/p&gt;&lt;p&gt;I ordered a caliper&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and measured the dimensions of the broken part, and took pictures of it from several angles for later reference. I took some notes:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/dc939c36.jpg&quot;&gt;
&lt;figcaption&gt;Picture of my notes on the measurements of the part&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Then, I used solvespace to design the following part:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/49cc6bba.png&quot;&gt;
&lt;figcaption&gt;Screenshot of the replacement part in solvespace&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;This was the third iteration — I printed one version, brought it out to the truck to compare with the broken part, made refinements to the design, then rinse and repeat. Here’s an earlier revision being compared with the broken piece:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/7407f94b.jpg&quot;&gt;
&lt;figcaption&gt;A hand holds up a 3D printed part for comparison with the original&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Finally, I arrived at a design I liked and sent it to the printer.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/e37b2b5a.jpg&quot;&gt;
&lt;figcaption&gt;Picture of my 3D printer working on the part&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I took some pliers to the remaining plastic bits from the broken part, and sawed off the rivets. I attached the replacement with superglue and ta-da!&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://redacted.moe/f/681bff34.jpg&quot;&gt;
&lt;figcaption&gt;Picture of the replacement part in place&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;If the glue fails, I’ll drill out what’s left of the rivets and secure it with screws. This may require another revision of the design, which will also give me a chance to address some minor shortcomings. I don’t expect to need this, though, because this is not part under especially high stress.&lt;/p&gt;&lt;p&gt;You can get the CAD files and an STL from my &lt;a href=&quot;https://git.sr.ht/~sircmpwn/open-dodge-d250&quot; target=&quot;_blank&quot;&gt;repository here&lt;/a&gt;, which I intend to keep updating as I learn more about this truck and encounter more fun problems to solve.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Designing-a-replacement-part-for-my-truck/</link>
        
        <pubDate>Wed, 25 Mar 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Designing-a-replacement-part-for-my-truck/</guid>
      </item>
    
      <item>
        
        
          <title>The reckless, infinite scope of web browsers</title>
          <description>
            &lt;p&gt;Since the first browser war between Netscape and Internet Explorer, web browsers have been using features as their primary means of competing with each other. This strategy of unlimited scope and perpetual feature creep is reckless, and has been allowed to go on for far too long.&lt;/p&gt;&lt;p&gt;I used wget to download all 1,217 of the &lt;a href=&quot;https://www.w3.org/TR/&quot; target=&quot;_blank&quot;&gt;W3C specifications&lt;/a&gt; which have been published at the time of writing&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, of which web browsers need to implement a substantial subset in order to provide a modern web experience. I ran a word count on all of these specifications. How complex would you guess the web is?&lt;/p&gt;&lt;p&gt;The total word count of the W3C specification catalogue is 114 million words at the time of writing. If you added the combined word counts of the C11, C++17, UEFI, USB 3.2, and POSIX specifications, all 8,754 published RFCs, and the combined word counts of everything on Wikipedia’s &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_longest_novels&quot; target=&quot;_blank&quot;&gt;list of longest novels&lt;/a&gt;, you would be 12 million words short of the W3C specifications.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;I conclude that &lt;strong&gt;it is impossible to build a new web browser&lt;/strong&gt;. The complexity of the web is &lt;em&gt;obscene&lt;/em&gt;. The creation of a new web browser would be comparable in effort to the Apollo program or the Manhattan project.&lt;/p&gt;&lt;p&gt;It is impossible to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Implement the web correctly&lt;/li&gt;&lt;li&gt;Implement the web securely&lt;/li&gt;&lt;li&gt;Implement the web &lt;strong&gt;at all&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Starting a bespoke browser engine with the intention of competing with Google or Mozilla is a fool’s errand. The last serious attempt to make a new browser, Servo, has become one part incubator for Firefox refactoring, one part playground for bored Mozilla engineers to mess with technology no one wants, and zero parts viable modern web browser. But WebVR is cool, right? Right?&lt;/p&gt;&lt;p&gt;The consequences of this are obvious. Browsers are the most expensive piece of software a typical consumer computer runs. They’re infamous for using all of your RAM, pinning CPU and I/O, draining your battery, etc. &lt;em&gt;Web browsers are responsible for more than 8,000 CVEs&lt;/em&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Because of the monopoly created by the insurmountable task of building a competitive alternative, browsers have also been free to stop being the “user agent” and start being the agents of their creators instead. Firefox is filling up with ads, tracking, and mandatory plugins. Chrome is used as a means for Google to efficiently track your eyeballs and muscle anti-technologies like DRM and AMP into the ecosystem.  The browser duopoly is only growing stronger, too, as Microsoft drops Edge and WebKit falls well behind its competition.&lt;/p&gt;&lt;p&gt;The major projects are open source, and usually when an open-source project misbehaves, we’re able to fork it to offer an alternative. But even this is an impossible task where web browsers are concerned. The number of W3C specifications grows at an average rate of 200 new specs per year, or about 4 million words, or about one POSIX every 4 to 6 months. How can a new team possibly keep up with this on top of implementing the outrageous scope web browsers already have &lt;em&gt;now&lt;/em&gt;?&lt;/p&gt;&lt;p&gt;The browser wars have been allowed to continue for far too long. They should have long ago focused on competing in terms of performance and stability, not in adding new web “features”. This is absolutely ridiculous, and it has to stop.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: I have prepared a write-up on &lt;a href=&quot;https://paste.sr.ht/~sircmpwn/13c1951014a256e9f551296a129bf6d10e9303dc&quot; target=&quot;_blank&quot;&gt;how I arrived at these word counts&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Reckless-limitless-scope/</link>
        
        <pubDate>Wed, 18 Mar 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Reckless-limitless-scope/</guid>
      </item>
    
      <item>
        
        
          <title>GitHub&apos;s new notifications: a case of regressive design</title>
          <description>
            &lt;p&gt;&lt;em&gt;Disclaimer: I am the founder of a company which competes with GitHub. However, I still use tools like GitHub, GitLab, and so on, as part of regular contributions to projects all over the FOSS ecosystem. I don’t dislike GitHub, and I use it frequently in my daily workflow.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;GitHub is rolling out a new notifications UI. A few weeks ago, I started seeing the option to try it. Yesterday, I received a warning that the old UI will soon be deprecated. At this pace, I would not be surprised to see the new UI become mandatory in a week or two. I’m usually optimistic about trying out new features, but this change worried me right away. I still maintain a few projects on GitHub, and I frequently contribute to many projects there. Using the notification page to review these projects is a ritual I usually conduct several times throughout the workday. So, I held my breath and tried it out.&lt;/p&gt;&lt;p&gt;The new UI looks a lot more powerful initially. The whole page is used to present your notifications, and there are a lot more buttons to click, many of them with cute emojis to quickly convey meaning. The page is updated in real-time, so as you interact with the rest of the website your notifications page in the other tab will be updated accordingly.&lt;/p&gt;&lt;p&gt;Let’s stop and review my workflow using the &lt;em&gt;old&lt;/em&gt; UI. I drew this beautiful graphic up in GIMP to demonstrate:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cmpwn.com/system/media_attachments/files/000/659/354/original/d9abc4befe1a074c.png&quot;&gt;&lt;/p&gt;&lt;p&gt;I open the page, then fix my eyes on the notification titles. I move my mouse to the right, and while reading titles I move the mouse down, clicking to mark any notifications as read that I don’t need to look at, and watching in my peripheral vision to see that the mouse hits its mark over the next button. The notifications are grouped by repository, so I can read the name of the repo then review all of its notifications in one go. The page is fairly narrow, so reading the titles usually leads my eyes naturally into reading any other information I might need, like the avatars of participants or age of the notification.&lt;/p&gt;&lt;p&gt;I made an equally beautiful picture for the new UI&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cmpwn.com/system/media_attachments/files/000/659/353/original/b15f20de0ae35cd3.png&quot;&gt;&lt;/p&gt;&lt;p&gt;This one is a lot harder to scan quickly or get into your muscle memory. The title of the notification no longer stands out, as it’s the same size as the name of the repo that was affected. They’re no longer grouped by repo, either, so I have to read both every time to get the full context. I then have to move my eyes &lt;em&gt;all the way&lt;/em&gt; across the page to review any of those other details, through vast fields of whitespace, where I can easily lose my place and end up on a different row.&lt;/p&gt;&lt;p&gt;Once I’ve decided what to do with it, I have to move my mouse over the row, and wait for the action buttons to appear. They were invisible a second ago, so I have to move my mouse again to get closer to the target. Clicking it will mark it as read. Then, because I have it filtered to unread (because “all” notifications is really &lt;em&gt;all&lt;/em&gt; notifications, and there’s no “new” notifications like the old UI had), the row disappears, making it difficult to undo if it was a mistake. Then I heave my eyes to the left again to read the next one.&lt;/p&gt;&lt;p&gt;This page is updated in real-time. In the old UI, after I had marked everything as read that I didn’t need to look at, I would middle click on each remaining notification to open it in a new tab. On the new real-time page, as soon as the other tab loads, the notification I clicked disappears (again, because I have it filtered to “unread”). This isn’t immediate, though — it takes at least as long as it takes for the new tab to load. Scanning the list and middle-clicking every other message becomes a Sisyphean task.&lt;/p&gt;&lt;p&gt;And the giant sticky header that follows you around! A whole 160 pixels, 14% of my vertical space, is devoted to a new header which shows up on the next page when I follow through a notification. And it’s implemented with JavaScript and done in a bizzare way, so writing a user style to get rid of it was rather difficult.&lt;/p&gt;&lt;p&gt;Aside: I tried adding a custom filter to show only pull requests, but it seems to silently fail, and I just see all of my notifications when I use it.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Anyway, we’re probably stuck with this. Now that they’ve announced the imminent removal of the old UI, we can probably assume that this feature is on the non-stop release train. Negative feedback almost never leads to cancelling the roll-out of a change, because the team’s pride is on the line.&lt;/p&gt;&lt;p&gt;I haven’t spoken to anyone who likes the new UI. Do you?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/GitHub-notifications/</link>
        
        <pubDate>Fri, 13 Mar 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/GitHub-notifications/</guid>
      </item>
    
      <item>
        
        
          <title>An open letter to Senator Bob Casey on end-to-end encryption</title>
          <description>
            &lt;p&gt;To Senator Bob Casey, I’m writing this open letter.&lt;/p&gt;&lt;p&gt;As your constituent, someone who voted for you in 2018, and an expert in software technology, I am disappointed in your support of the EARN IT Act. I am aware that encryption is a challenging technology to understand, even for us software engineers, and that it raises difficult problems for the legislature. The EARN IT Act does not protect our children, and it has grave implications for the freedoms of our citizens.&lt;/p&gt;&lt;p&gt;The mathematics underlying strong end-to-end encryption have been proven to be unbreakable. Asking service providers to solve them or stop using it is akin to forcing us to solve time travel or quit recording history. Banning the use of a technology without first accomplishing a sisyphean task is equivalent to banning the technology outright. Ultimately, these efforts are expensive and futile. The technology necessary to implement unbreakable encryption can be described succinctly on a single 8.5”x11” sheet of paper. I would be happy to send such a paper to your office, if you wish. The cat is out of the bag: encryption is not a secret, and its use to protect our citizens is a widespread industry standard. Attempting to ban it is equivalent to trying to ban algebra or trigonometry.&lt;/p&gt;&lt;p&gt;Citizen use of end-to-end encryption is necessary to uphold our national security. One way that child abuse material is often shared is via the Tor secure internet network. This system utilizes strong end-to-end encryption to secure the communications of its users, which makes it well-suited to hiding the communications of child abusers. However, the same guarantees that enable the child abusers to securely share materials are also essential for journalists, activists, watchdog groups - and for our national security. The technology behind Tor was designed by the US Navy and DARPA and the ability for the public to use it to secure their communications is essential to the network’s ability to delivery on its national security guarantees as well.&lt;/p&gt;&lt;p&gt;Protecting our children is important, but this move doesn’t help. Breaking end-to-end encryption is no substitute for good police work and effective courts. Banning end-to-end encryption isn’t going to make it go away - the smart criminals are still going to use it to cover their tracks, and law enforcement still needs to be prepared to solve cases with strong encryption involved. Even on the Tor network, where strong end-to-end encryption is utilized, many child abusers have been caught and brought to justice thanks to good investigative work. It’s often difficult to conduct an investigation within the limits of the law and with respect to the rights of our citizens, but it’s necessary for law enforcement to endure this difficulty to protect our freedom.&lt;/p&gt;&lt;p&gt;End-to-end encryption represents an important tool for the preservation of our fundamental rights, as enshrined in the bill of rights. Time and again, our alleged representatives levy attacks on this essential technology. It doesn’t get any less important each time it’s attacked - rather, the opposite seems to be true. On the face of it, the EARN IT Act appears to use important and morally compelling problems of child abuse as a front for an attack on end-to-end encryption. Using child abuse as a front to attack our fundamental right to privacy is reprehensible, and I’m sure that you’ll reconsider your position.&lt;/p&gt;&lt;p&gt;As freedom of the press is an early signal for the failure of democracy and rise of tyranny, so holds for the right to encrypt. I am an American, I am free to speak my mind. I am free to solve a simple mathematical equation which guarantees that my thoughts are shared only with those I choose. The right to private communications is essential to a functioning democracy, and if you claim to represent the American people, you must work to defend that right.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Open-letter-to-Senator-Casey/</link>
        
        <pubDate>Sat, 07 Mar 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Open-letter-to-Senator-Casey/</guid>
      </item>
    
      <item>
        
        
          <title>The Abiopause</title>
          <description>
            &lt;p&gt;The sun has an influence on its surroundings. One of these is in the form of small particles that are constantly ejected from the sun in all directions, which exerts an outward pressure, creating an expanding sphere of particles that moves away from the sun. These particles are the solar wind. As the shell of particles expands, the density (and pressure) falls. Eventually the solar wind reaches the &lt;em&gt;interstellar medium&lt;/em&gt; — the space between the stars — which, despite not being very dense, is not empty. It exerts a pressure that pushes inwards, towards the sun.&lt;/p&gt;&lt;p&gt;Where the two pressures balance each other is an interesting place. The sphere up to this point is called the &lt;em&gt;heliosphere&lt;/em&gt; — which can be roughly defined as the zone in which the influence of the sun is the dominant factor. The &lt;em&gt;termination shock&lt;/em&gt; is where the change starts to occur. The plasma from the sun slows, compresses, and heats, among other changes. The physical interactions here are interesting, but aren’t important to the metaphor. At the termination shock begins the &lt;em&gt;heliosheath&lt;/em&gt;. This is a turbulent place where particles from the sun and from the interstellar medium mix. The interactions in this area are complicated and interesting, you should read up about it later.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/_FIT.svg&quot; alt=&quot;Picture of a faucet pouring into a sink&quot;&gt;
&lt;figcaption&gt;Yanpas via Wikimedia Commons, CC-BY-SA&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Finally, we reach the &lt;em&gt;heliopause&lt;/em&gt;, beyond which the influence of the interstellar medium is dominant. Once crossing this threshold, you are said to have left the solar system. The Voyager 1 space probe, the first man-made object to leave the solar system, crossed this point on August 25th, 2012. Voyager 2 completed the same milestone on November 12th, 2018&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;In the world of software, the C programming language clearly stands out as the single most important and influential programming language.  Everything forming the critical, foundational parts of your computer is written in it: kernels, drivers, compilers, interpreters, runtimes, hypervisors, databases, libraries, and more are almost all written in C.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; For this reason, any programming language which wants to get anything useful done is certain to support a C FFI (foreign function interface), which will allow programmers to communicate with C code from the comfort of a high-level language. No other language has the clout or ubiquity to demand this level of deference from everyone else.&lt;/p&gt;&lt;p&gt;The way that an application passes information back and forth with its subroutines is called its &lt;em&gt;ABI&lt;/em&gt;, or application binary interface. There are a number of ABIs for C, but the most common is the System-V ABI, which is used on most modern Unix systems. It specifies details like which function parameters to put in which registers, what goes on the stack, the structure and format of these values, and how the function returns a value to the caller. In order to interface with C programs, the FFI layers in other programs have to utilize this ABI to pass information to and from C functions.&lt;/p&gt;&lt;p&gt;Other languages often have their own ABIs. C, being a different programming language from $X, naturally has different semantics. The particular semantics of C don’t necessarily line up to the semantics the language designers want $X to have, so the typical solution is to define functions with C “linkage”, which means they’re called with the C ABI. It’s from this that we get keywords like &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;/code&gt; (C++, Rust), Go’s Cgo tooling, &lt;code&gt;[DllImport]&lt;/code&gt; in C#, and so on. Naturally, these keywords come with a lot of constraints on how the function works, limiting the user to the mutually compatible subset of the two ABIs, or else using some kind of translation layer.&lt;/p&gt;&lt;p&gt;I like to think of the place where this happens as the “abiopause”, and draw comparisons with the solar system’s heliopause. Within the “abiosphere”, the programming language you’re using is the dominant influence. The idioms and features of the language are used to their fullest extent to write idiomatic code. However, the language’s sphere of influence is but a bubble in a sea of C code, and the interface between these two areas of influence is often quite turbulent. Directly using functions with C linkage from the abiosphere is not pleasant, as the design of good C APIs do not match the semantics of good $X APIs. Often there are layers to this transition, much like our solar system, where some attempt is made to wrap the C interface in a more idiomatic abstraction.&lt;/p&gt;&lt;p&gt;I don’t really like this boundary, and I think most programmers who have worked here would agree. If you like C, you’re stuck either writing bad C code or using poorly-suited tools to interface badly with an otherwise good API. If you like $X, you’re stuck writing very non-idiomatic $X code to interface with a foreign system. I don’t know how to fix this, but it’s interesting to me that the “abiopause” appears to be an interface full of a similar turbulence and complexity as we find in the heliopause.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Abiopause/</link>
        
        <pubDate>Tue, 03 Mar 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Abiopause/</guid>
      </item>
    
      <item>
        
        
          <title>Thoughts on performance &amp; optimization</title>
          <description>
            &lt;p&gt;The idea that programmers ought to or ought not to be called “software engineers” is a contentious one. How you approach optimization and performance is one metric which can definitely push my evaluation of a developer towards the engineering side. Unfortunately, I think that a huge number of software developers today, even senior ones, are approaching this problem poorly.&lt;/p&gt;&lt;p&gt;Centrally, I believe that you cannot effectively optimize a system which you do not understand. Say, for example, that you’re searching for a member of a linked list, which is an O(n) operation. You know this can be improved by switching from a linked list to a sorted array and using a binary search. So, you spend a few hours refactoring, commit the implementation, and… the application is no faster. What you failed to consider is that the lists are populated from data received over the network, whose latency and bandwidth constraints make the process much slower than any difference made by the kind of list you’re using.  If you’re not optimizing your bottleneck, then you’re wasting your time.&lt;/p&gt;&lt;p&gt;This example seems fairly obvious, and I’m sure you, our esteemed reader, would not have made this mistake. In practice, however, the situation is usually more subtle. Thinking about your code really hard, making assumptions, and then acting on them is not the most effective way to make performance improvements. Instead, we apply the scientific method: we think really hard, &lt;em&gt;form a hypothesis&lt;/em&gt;, make predictions, test them, and then apply our conclusions.&lt;/p&gt;&lt;p&gt;To implement this process, we need to describe our performance in factual terms. All software requires a certain amount of resources — CPU time, RAM, disk space, network utilization, and so on. These can also be described over time, and evolve as the program takes on different kinds of work. For example, we could model our program’s memory use as bytes allocated over time, and perhaps we can correlate this with different stages of work — “when the program starts task C, the rate of memory allocation increases by 5MiB per second”. We identify bottlenecks — “this program’s primary bottleneck is disk I/O”. When we hit performance problems, then we know that we need to upgrade to SSDs, or predict what reads will be needed later and prep them in advance, cache data in RAM, etc.&lt;/p&gt;&lt;p&gt;Good optimizations are based on factual evidence that the program is not operating within its constraints in certain respects, then improving on those particular problems. You should always conduct this analysis before trying to solve your problems. I generally recommend conducting this analysis in advance, so that you can predict performance issues before they occur, and plan for them accordingly. For example, if you know that your disk utilization grows by 2 GiB per day, and you’re on a 500 GiB hard drive, you’ve got about 8 months to plan your next upgrade, and you shouldn’t be surprised by an ENOSPC when the time comes.&lt;/p&gt;&lt;p&gt;For CPU bound tasks, this is also where a general understanding of the performance characteristics of various data structures and algorithms is useful. When you know you’re working on something which &lt;em&gt;will become&lt;/em&gt; the application’s bottleneck, you would be wise to consider algorithms which can be implemented efficiently. However, it’s equally important to re-prioritize performance when you’re not working on your bottlenecks, and instead consider factors like simplicity and conciseness more seriously.&lt;/p&gt;&lt;p&gt;Much of this will probably seem obvious to many readers. Even so, I think the general wisdom described here is institutional, so it’s worth writing down. I also want to call out some specific behaviors that I see in software today which I think don’t take this well enough into account.&lt;/p&gt;&lt;p&gt;I opened by stating that I believe that you cannot effectively optimize a system which you do not understand. There are two groups of people I want to speak to with this in mind: library authors (especially the standard library), and application programmers. There are some feelings among library authors that libraries should be fairly opaque, and present high-level abstractions over their particular choices of algorithms, data structures, and so on. I think this represents a fundamental lack of trust with the programmer downstream. Rather than write idiot-proof abstractions, I think it’s better to trust the downstream programmer, explain to them how your system works, and equip them with the tools to audit their own applications. After all: your library is only a small component of &lt;em&gt;their&lt;/em&gt; system, not yours — and you cannot optimize a system you don’t understand.&lt;/p&gt;&lt;p&gt;And to the application programmer, I urge you to meet your dependencies in the middle. Your entire system is your responsibility, including your dependencies. When the bottleneck lies in someone else’s code, you should be prepared to dig into their code, patch it, and send a fix upstream, or to restructure your code to route the bottleneck out. Strive to understand how your dependencies, up to and including the stdlib, compiler, runtime, kernel, and so on, will perform in your scenario. And again to the standard library programmer: help them out by making your abstractions thin, and your implementations simple and debuggable.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Thoughts-on-performance/</link>
        
        <pubDate>Fri, 21 Feb 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Thoughts-on-performance/</guid>
      </item>
    
      <item>
        
        
          <title>Fucking laptops</title>
          <description>
            &lt;p&gt;The best laptop ever made is the ThinkPad X200, and I have two of them. The caveats are: I get only 2-3 hours of battery life even with conservative use; and it struggles to deal with 1080p videos.&lt;/p&gt;&lt;p&gt;The integrated GPU, Bluetooth and WiFi, internal sensors, and even the fingerprint reader can all be driven by the upstream Linux kernel. In fact, the hardware is so well understood that I have successfully used almost all of the laptop’s features on Linux, FreeBSD, NetBSD, Minix, Haiku, and Plan 9. Plan fucking 9. It can run coreboot, too. The back of the laptop has all of the screws (Phillips head) labelled so you know which to remove to service which parts. User replacable parts include the screen, keyboard (multiple layouts are available and are interchangeable), the RAM, hard drive (I put a new SSD in one of mine a few weeks ago, and it took about 30 seconds) — actually, there are a total of 26 replacable parts in this laptop.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; There is a detailed 278-page service manual to assist you or your local repair tech in addressing any problems that arise.&lt;/p&gt;&lt;p&gt;They’re quite durable, too. Mine still looks like it just rolled off the assembly line yesterday. In fact, it was built 12 years ago.&lt;/p&gt;&lt;p&gt;The X200 was made in 2008. In the time since, the modern laptops’ battery life and video decoding performance has improved. In every other respect, the market is regressive, half-assed garbage.&lt;/p&gt;&lt;p&gt;I am usually near power, so I’ve been reasonably happy even with the pithy battery life of the X200. I also have a T520, which sucks in its own way&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, but can decode 1080p videos just fine. I generally don’t need a lot of power - compiling most programs is fast enough that I don’t really notice, especially with incremental compilation, and for any large workloads I just SSH out into a build server somewhere. However, I’ve been planning some astronomy outings lately, and the battery life matters for this - so I was looking for a laptop I could run &lt;a href=&quot;https://stellarium.org/&quot; target=&quot;_blank&quot;&gt;Stellarium&lt;/a&gt; on to drive my telescope into the wee hours of the night.&lt;/p&gt;&lt;p&gt;It has since come to my attention that in 2020, every laptop &lt;em&gt;still&lt;/em&gt; fucking sucks. Even the ones people pretend to like have crippling, egregious flaws. The Dell XPS series has a firmware so bad that its engineers should be strung up in the town square for building it - if yours works, it’s because you were &lt;em&gt;lucky&lt;/em&gt;. System76 laptops are bulky and priced at 2x or 3x what they’re worth. Same goes for Purism, plus a company I have no desire to support any longer, and they’re out of stock anyway. Pine64 requires nonfree blobs, patched kernels, and booting up ARM devices is a fucking nightmare, and they’re out of stock anyway. The Star Lite looks promising, but they’re out of stock too. Huewei laptops are shameless Macbook ripoffs with the same shitty keyboards, and you can’t buy them in the US anyway. Speaking of Macbooks, even Apple fanboys are fed up with them these days.&lt;/p&gt;&lt;p&gt;The laptop market is in an atrocious state, folks. If you work at any of these companies and you’re proud of the garbage you’re shipping, then I’m disappointed in you. Come on, let’s get our shit together and try to make a laptop which is &lt;em&gt;at least&lt;/em&gt; as good as the 12 year-old one I’m stuck with now.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Fucking-laptops/</link>
        
        <pubDate>Tue, 18 Feb 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Fucking-laptops/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, February 2020</title>
          <description>
            &lt;p&gt;Today I thought it’d try out something new: I have an old family recipe simmering on the stove right now, but instead of beef I’m trying out impossible beef. It cooked up a bit weird — it doesn’t brown up in the same way I expect of ground beef, and it made a lot more fond than I expected. Perhaps the temperature is too high? We’ll see how it fares when it’s done. In the meanwhile, let’s get you up to speed on my free software projects.&lt;/p&gt;&lt;p&gt;First, big thanks to everyone who stopped by to say “hello” at FOSDEM! Putting faces to names and getting to know everyone on a personal level is really important to me, and I would love FOSDEM even if that was all I got out of it. Got a lot of great feedback on the coming plans for SourceHut and aerc, too.&lt;/p&gt;&lt;p&gt;That aside, what’s new? On the Wayland scene, the long-promised Sway 1.3^W1.4 was finally released, and with it wlroots 0.10.0. I’ve been talking it up for a while, so I won’t bore you by re-listing all of the cool stuff in this release - it’ll suffice to say that I think you’ll enjoy it. The related tools — swayidle, swaylock, swaybg — all saw releases around the same time. The other release this month was scdoc 1.10.1, which was a simple patch release. Beyond releases, there’s been some Wayland development work as well: wev received a simple bugfixes, and casa’s OpenGL-based renderer rewrite has been completed nicely.&lt;/p&gt;&lt;p&gt;aerc progresses nicely this month as well, thanks to the support of its many dedicated contributors. Many bugfixes have landed, alongside contextual configuration options — so you can have different config settings, for example, when you have an email selected whose subject line matches a regex. A series of notmuch patches should be landing soon as well. himitsu has also seen slow progress — this pace being deliberate, as this is security-sensitive software. Several bugs have been fixed in the existing code, but there are a few more to address still. imrsh also had a little bit of improvement this month, as I started down the long road towards properly working UTF-8 support.&lt;/p&gt;&lt;p&gt;SourceHut improvements have also landed recently. I did some work shoring up our accessibility standards throughout the site, and SourceHut is now fully complaint with the WCAG accessibility guidelines. We now score 100% on standard performance, accessibility, and web standards compliance tests. SourceHut is the lightest weight, most usable forge. I recently fixed a bug report from a Lynx, user, too 😉 In terms of feature development, the big addition this month is support for attaching files to annotated git tags, so you can attach binaries, PGP signatures, and so on to your releases. More cool SourceHut news is coming in the post to sr.ht-announce later today.&lt;/p&gt;&lt;p&gt;This month’s update is a little bit light on content, I’ll admit. Between FOSDEM and taking some personal time, I’ve had less time for work this month. However, there’s another reason: I have a new secret project which I’ve been working on. I intend to keep this project under wraps for a while still, because I don’t want people to start using it before I know if it’s going to pan out or not. This project is going to take a lot of time to complete, so I hope you’ll bear with me for a while and trust that the results will speak for themselves. As always, thank you for your support, and I’m looking forward to another month of awesome FOSS work.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-February-2020/</link>
        
        <pubDate>Sat, 15 Feb 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-February-2020/</guid>
      </item>
    
      <item>
        
        
          <title>Dependencies and maintainers</title>
          <description>
            &lt;p&gt;I’m 34,018 feet over the Atlantic at the moment, on my way home from FOSDEM. It was as always a lovely event, with far too many events of interest for any single person to consume. One of the few talks I was able to attend&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; left a persistent worm of thought in my brain. This talk was put on by representatives of Microsoft and GitHub and discusses whether or not there is a sustainability problem in open source (&lt;a href=&quot;https://fosdem.org/2020/schedule/event/foss_sustainability_issues/&quot; target=&quot;_blank&quot;&gt;link&lt;/a&gt;). The content of the talk, interpreted within the framework in which it was presented, was moderately interesting. It was more fascinating to me, however, as a lens for interpreting GitHub’s (and, indirectly, Microsoft’s) approach to open source, and of the mindset of developers who approach problems in the same ways.&lt;/p&gt;&lt;p&gt;The presenters drew attention to a few significant crises open-source communities have faced in recent years — left-pad, in which a trivial library was removed from npm and was unknowingly present in thousands of dependency graphs; event-stream, in which a maintainer transferred project ownership to an unknown individual who added crypto mining; and heartbleed, in which a bug in a critical security library caused mass upgrades and panic — and asks whether or not these can be considered sustainability issues. The talk has a lot to dissect and will frame my thinking and writings for a while. Today I’ll focus on one specific problem, which I called attention to during the Q&amp;A.&lt;/p&gt;&lt;p&gt;At a few points, the presenters spoke from the perspective of a business which depends on up to thousands of open-source libraries or tools. In such a context, how do you prioritize which of your thousands of dependencies requires attention, for financial support, contributions upstream, and so on? I found this worldview dissonant, and asked the following question: “why do you have thousands of dependencies in the first place?” Because this approach seems to be fast becoming the norm, this may seem like a stupid question.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; If any Node developers are reading, scan through your nearest node_modules directory and see how many of these dependencies you’ve even heard of.&lt;/p&gt;&lt;p&gt;Such an environment is primed to fail in the ways enumerated by this talk. Consider the case of the maintainer who lost interest and gave their project to an untrusted third party. If I had depended on this library, I would have noticed long ago that the project was effectively unmaintained. It’s likely that I or my peers would have sent patches to this project, given that bugfixes would have stopped coming from upstream. We would be aware of the larger risk this project posed, and have studied alternatives. Earlier than that, I would probably have lent my ear to the maintainer to vent their frustrations, and offered my help where possible.&lt;/p&gt;&lt;p&gt;For most of my projects, I can probably list the entire dependency graph, including transitive dependencies, off of the top of my head. I can name most of their maintainers, and many of their contributors. I have shaken the hands of these people, shared drinks and meals with them, and count many of them among my close friends. The idea of depending on a library I’ve never heard of, several degrees removed via transitive dependencies, maintained by someone I’ve never met and have no intention of speaking to, is &lt;em&gt;absolutely nuts&lt;/em&gt; to me. I know of these problems well in advance because I know the people affected as my friends. If someone is frustrated or overworked, I’m right there with them trying to find solutions and correct the over-burden. If someone is in dire financial straights, I’m helping them touch up their resume and introducing them to companies that I know are looking for their skillset, or helping them work on more sustainable sources of donations and grants. They do the same for me, and for each other.&lt;/p&gt;&lt;p&gt;Quit treating open-source projects like a black box that conveniently solves your problem. Engage with the human beings who work on it, participate in the community, and &lt;em&gt;make&lt;/em&gt; it healthy and sustainable. You shouldn’t be surprised when the 3 AM alarm goes off if the most you see of a project is a line in your &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Dependencies-and-maintainers/</link>
        
        <pubDate>Thu, 06 Feb 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Dependencies-and-maintainers/</guid>
      </item>
    
      <item>
        
        
          <title>KnightOS was an interesting operating system</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://knightos.org&quot; target=&quot;_blank&quot;&gt;KnightOS&lt;/a&gt; is an operating system I started writing about 10 years ago, for Texas Instruments line of z80 calculators — the TI-73, TI-83+, TI-84+, and similar calculators are supported. It still gets the rare improvements, but these days myself and most of the major contributors are just left with starry eyed empty promises to themselves that one day they’ll do one of those big refactorings we’ve been planning… for 4 or 5 years now.&lt;/p&gt;&lt;p&gt;Still, it was a really interesting operating system which was working under some challenging constraints, and overcame them to offer a rather nice Unix-like environment, with a filesystem, preemptive multiprocessing &lt;em&gt;and&lt;/em&gt; multithreading, assembly and C programming environments, and more. The entire system was written in handwritten z80 assembly, almost 50,000 lines of it, on a compiler toolchain we built from scratch.&lt;/p&gt;&lt;p&gt;There was only 64 KiB of usable RAM. The kernel stored &lt;em&gt;all&lt;/em&gt; of its state in 1024 bytes of statically allocated RAM. Many subsystems used overlapping parts of this memory, which was carefully planned for to avoid conflicts. The userspace memory allocator used a simple linked list for tracking allocations, to minimize the overhead of each allocation and maximize the usable space for userspace programs. There was no MMU in the sense that we have on modern computers, so any program could freely overwrite any other programs. In fact, the “userspace” task switching GUI would read the kernel’s process table directly to make a list of running programs.&lt;/p&gt;&lt;p&gt;The non-volatile storage was NOR Flash, which presents some interesting constraints. In the worst case we only had 512 KiB of storage, and even in the best case just 4MiB (this for a device released in 2013). This space was shared with the kernel, whose core code was less than 4KiB, and including high-address subsystems still clocked in at less than 16KiB. Due to the constraints of NOR Flash, a custom filesystem was designed which did all daily operations by only &lt;em&gt;resetting&lt;/em&gt; bits in the underlying storage. In order to &lt;em&gt;set&lt;/em&gt; any bits, we had to set the entire 64 KiB sector to 1. Overhead was also kept to a bare minimum here to maximize storage space available to users.&lt;/p&gt;&lt;p&gt;Writing to Flash storage also renders it unreadable while the operation is in progress. The kernel normally executes directly from Flash, resident at the bottom of the memory. Therefore, in order to modify Flash, the kernel’s Flash driver copies part of itself to RAM, jumps to it, and then jumps back after the operation is complete. Recall that all of the kernel’s memory is statically allocated, and there’s not much of it — we used only 128 bytes for the code which runs in RAM, and it’s shared with some other stuff that we had to plan around. In order to meet these constraints, we employ &lt;em&gt;self modifying code&lt;/em&gt; — the Flash driver copies some of itself into RAM, then pre-computes some information and &lt;em&gt;modifies&lt;/em&gt; that machine code in-place before jumping to it.&lt;/p&gt;&lt;p&gt;We also had some basic networking support. The calculator has a 2.5mm jack, similar to headphone jacks — if you had a 3.5mm adapter, we had a music player which would play MIDI or WAV files. The kernel had direct control of the voltages on the ring and tip, and had to bitbang them directly in software&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Based on this we built some basic networking support, which supported calculator-to-calculator and calculator-to-PC information exchange. Later models had a mini-USB controller (which, funnily enough, can also be bitbanged in software), but we never ended up writing a driver for it.&lt;/p&gt;&lt;p&gt;The KnightOS kernel also includes some code which is the first time I ever wrote &lt;a href=&quot;https://github.com/KnightOS/kernel/blob/e257f54e021ee743306a2a4a5a152860728fb3f8/src/00/restarts.asm#L129-L130&quot; target=&quot;_blank&quot;&gt;“here be dragons”&lt;/a&gt; into a comment, and I don’t think I’ve topped it since.&lt;/p&gt;&lt;p&gt;Despite these constraints, KnightOS is completely booted up to a useful Unix-like (with a graphical interface) faster than you can lift your finger off of the power button. The battery could last the entire semester, if you’re lucky. Can the device you’re reading this on claim the same?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;&lt;video controls src=&quot;https://yukari.sr.ht/knightos.webm&quot;&gt;&lt;/video&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/KnightOS-was-interesting/</link>
        
        <pubDate>Mon, 27 Jan 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/KnightOS-was-interesting/</guid>
      </item>
    
      <item>
        
        
          <title>The happinesses and stresses of full-time FOSS work</title>
          <description>
            &lt;p&gt;In the past few days, several free software maintainers have come out to discuss the stresses of their work. Though the timing was suggestive, my article last week on the philosophy of project governance was, at best, only tangentially related to this topic - I had been working on that article for a while. I do have some thoughts that I’d like to share about what kind of stresses I’ve dealt with as a FOSS maintainer, and how I’ve managed (or often mismanaged) it.&lt;/p&gt;&lt;p&gt;February will mark one year that I’ve been working on self-directed free software projects full-time. I was planning on writing an optimistic retrospective article around this time, but given the current mood of the ecosystem I think it would be better to be realistic. In this stage of my career, I now feel at once happier, busier, more fulfilled, more engaged, more stressed, and more depressed than I have at any other point in my life.&lt;/p&gt;&lt;p&gt;The good parts are numerous. I’m able to work on my life’s passions, and my projects are in the best shape they’ve ever been thanks to the attention I’m able to pour into them. I’ve also been able to do more thoughtful, careful work; with the  extra time I’ve been able to make my software more robust and reliable than it’s ever been. The variety of projects I can invest my time into has also increased substantially, with what was once relegated to minor curiosities now receiving a similar amount of attention as my larger projects were receiving in my spare time before. I can work from anywhere in the world, at any time, not worrying about when to take time off and when to put my head down and crank out a lot of code.&lt;/p&gt;&lt;p&gt;The frustrations are numerous, as well. I often feel like I’ve bit off more than I can chew. This has been the default state of affairs for me for a long time; I’m often neglecting half of my projects in order to obtain progress by leaps and bounds in just a few. Working on FOSS full-time has cast this model’s disadvantages into greater relief, as I focus on a greater breadth of projects and spend more time on them.&lt;/p&gt;&lt;p&gt;The attention and minor fame I’ve received as a result of my prolific efforts also has profound consequences. On the positive line of thought, I’m somewhat embarrassed to admit that I’ve noticed my bug reports and feature requests on random projects (or even my own projects) being taken more seriously now, which is almost certainly more related to name recognition than merit. I often receive thanks and words of admiration from my… fans? I guess I have those now. Sometimes these are somewhat unwelcome, with troubled individuals writing difficult to decipher half-rants laden with strange praises and bizarre questions. Other times I’m asked out of the blue to join a discussion I was unaware of, to comment on some piece of technology I’ve never used or to take a stand on some argument which I wasn’t privy to. I don’t enjoy these kinds of comments. But, they’re not far removed from the ones I like - genuine, thoughtful praise arrives in my inbox fairly often and it makes the job a lot more worthwhile.&lt;/p&gt;&lt;p&gt;Of course, a similar sort of person exists on the opposite extreme. There are many people who hate my guts and anything I’ve ever worked on, and who’ll go out of their way to let me and anyone else who’ll listen to them know how they feel. Of course, I have &lt;em&gt;earned&lt;/em&gt; the ire of no small number of people, and I regret many of these failed interpersonal relationships. These cases are in the minority, however - most of the people who will tell tales of my evil are people who I’ve never met. There’s a lot of spaces online that I just won’t visit anymore because of them. As for the less extreme of this sort of person, I’ll also reiterate what others have said - the negative effects of entitled, arrogant, or outright toxic users is profound. Don’t be that person.&lt;/p&gt;&lt;p&gt;In either case, I can never join new communities on the same terms as anyone else does. At least one person in every new community already has some preconception of me when I arrive. Often I think about making an alias just to enjoy the privilege of anonymity again.&lt;/p&gt;&lt;p&gt;A great help has been my daily interactions with the many friends and colleagues who are dear to me. I’ve made lifelong friends of many of the people I’ve met through these projects.  Thanks to FOSS, I have met an amazing number of kind, talented, generous people.  Every day, I’m thankful to and amazed by the hundreds of people who have found my ideas compelling, and who come together to contribute their own ideas and set aside their precious time to work together realizing our shared dreams. If I’m feeling blue, often all it takes to snap me out of it is to reflect on the gratitude I feel for these wonderful people. I’ll never be able to thank my collaborators enough, but hell, I could stand to do it some more anyway.&lt;/p&gt;&lt;p&gt;I also have mixed feelings about how &lt;em&gt;busy&lt;/em&gt; I am. Every day I wake up to a hundred new emails, delete half of them, and spend 3-4 hours working on the rest. Patches, questions, support inquiries, monitoring &amp; reports, it’s endless. On top of that, I have dozens of things I already need to work on. The CI work distribution algorithm needs to be completely redone; I need to provision new hardware — oh yeah, and, the hardware that I need ran into shipping issues, again; I need to improve monitoring; I need to plan for FOSDEM; I need to finish the Wayland book; I need to figure out the memory issues in himitsu — not to mention write the rest of the software; I need to file taxes, twice as much work when you own a business; I need to implement data export &amp; account deletion; I need to finish the web-driven patch review UI; I need to finish writing docs for Alpine; I have to work more on the PinePhone; I have a legacy server which needs to be overhauled and is now on the clock because of ACMEv1; names.sr.ht needs to be finished…&lt;/p&gt;&lt;p&gt;Not to mention the tasks which have been on hold for longer now than they’ve been planned for in the first place. Alpine is still going to have hundreds of Python 2 packages by EoL; the ppc64le server is gathering dust in the datacenter; there’s been some bug with fosspay for several months, in which it doesn’t show Patreon figures unless I reboot the process every now and then; RISC-V work is stalled because the work is currently blocked by a large problem that I can’t automate; the list of blog posts I want to write is well over 100 entries long. There are &lt;em&gt;several dozen&lt;/em&gt; other loose ends I haven’t mentioned here but am painfully aware of anyway.&lt;/p&gt;&lt;p&gt;That’s not even considering any personal goals, which I have vanishingly little time for. I get zero exercise, and though my diet is mostly reasonable the majority of it is delivery unless I get the odd 2 hours to visit the grocery store. That is, unless I want to spend those 2 hours with my friends, which means it’s back to delivery. My dating life is almost nonexistent. I want to spend more time studying Japanese, but it’s either that or keeping up with my leisure reading. Lofty goals of also studying Chinese or Arabic are but dust in the wind. I’m addicted to caffeine, again.&lt;/p&gt;&lt;p&gt;There have been healthy ways and unhealthy ways of dealing with the occasional feelings of being overwhelmed by all of this. The healthier ways have included taking walks, reading my books, spending a few minutes with my cat, doing chores, and calling my family to catch up. Less healthy ways have included walking to the corner store to buy unhealthy comfort foods, consuming alcohol or weed too much or too often, getting in stupid internet arguments, being mean to my friends and colleagues, and googling myself to read negative comments.&lt;/p&gt;&lt;p&gt;Despite being swamped with all of this work, it’s all work that I love. I love writing code, and immeasurably more so when writing &lt;em&gt;my&lt;/em&gt; code. Sure, there are tech debt skeletons in the closet here and they’re keeping me awake at night, but on the whole I feel lucky to be able to write the software I want to write, the way I want to write it. I’ve been trying to do that my entire life — writing code for someone else has always been a huge drain on my emotional well-being.  That’s why I worked on my side projects in the first place, to have an outlet through which I could work on self-directed projects without making compromises for some arbitrary deadline.&lt;/p&gt;&lt;p&gt;When I’m in the zone, writing lots of code for a project I’m interested in, knowing it’s going to have a meaningful impact on my users, knowing that it’s being written under my terms, it’s the most rewarding work I’ve ever done. I get to do that every day.&lt;/p&gt;&lt;p&gt;This isn’t the retrospective I wanted to write, but it’s nice to drop the veneer for a few minutes and share an honest take on what this is like. This year has been nothing like what I expected it to be - it’s both terrible and wonderful and very busy, very goddamn busy. In any case, I’m extremely grateful to be here doing it, and it’s thanks to many, many supportive people - users, contributors, co-maintainers, and friends. Thank you, thank you, thank you, thank you.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Stress-and-happiness/</link>
        
        <pubDate>Tue, 21 Jan 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Stress-and-happiness/</guid>
      </item>
    
      <item>
        
        
          <title>A philosophy of project governance</title>
          <description>
            &lt;p&gt;I’ve been in the maintainer role for dozens of projects for a while now, and have moderated my fair share of conflicts. I’ve also been on the other side, many times, as a minor contributor watching or participating in conflict within other projects. Over the years, I’ve developed an approach to project governance which I believe is lightweight, effective, and inclusive.&lt;/p&gt;&lt;p&gt;I hold the following axioms to be true:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Computer projects are organized by humans, creating a social system.&lt;/li&gt;&lt;li&gt;Social systems are fundamentally different from computer systems.&lt;/li&gt;&lt;li&gt;Objective rules cannot be programmed into a social system.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;And the following is true of individuals within those systems:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Project leadership is in a position to do anything they want.&lt;/li&gt;&lt;li&gt;Project leadership will ultimately do whatever they want, even if they have to come up with an interpretation of the rules which justifies it.&lt;/li&gt;&lt;li&gt;Individual contributors who have a dissonant world-view from project leadership will never be welcome under those leaders.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Any effective project governance model has to acknowledge these truths. To this end, the simplest effective project governance model is a BDFL, which scales a lot further than people might expect.&lt;/p&gt;&lt;p&gt;The BDFL (Benevolent Dictator for Life) is a term which was first used to describe Python’s governance model with Guido van Rossum at the helm. The “for life” in BDFL is, in practice, until the “dictator” resigns from their role. Transfers of power either involve stepping away and letting lesser powers decide between themselves how to best fill the vacuum, or simply directly appointing a successor (or successors). In this model, a single entity is in charge — often the person who started the project, at first — and while they may delegate their responsibilities, they ultimately have the final say in all matters.&lt;/p&gt;&lt;p&gt;This decision-making authority derives from the BDFL. Consequently, the project’s values are a reflection of that BDFL’s values. Conflict resolution and matters of exclusion or inclusion of specific people from the project is the direct responsibility of the BDFL. If the BDFL delegates this authority to other groups or project members, that authority derives from the BDFL and is exercised at their leisure, on their terms. In practice, for projects of a certain size, most if not all of the BDFL’s authority is delegated across many people, to the point where the line between BDFL and core contributor is pretty blurred. The relationships in the project are built on trust between individuals, not trust in the system.&lt;/p&gt;&lt;p&gt;As a contributor, you should evaluate the value system of the leadership and make a personal determination as to whether or not it aligns with your own. If it does, participate. If it does not, find an alternative or fork the project.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Consider the main competing model: a Code of Conduct as the rule of law.&lt;/p&gt;&lt;p&gt;These attempt to boil subjective matters down into objectively enforcible rules. Not even in sovereign law do we attempt this. Programmers can easily fall into the trap of thinking that objective rules can be applied to social systems, and that they can deal with conflict by executing a script. This is quite untrue, and attempting to will leave loopholes big enough for bad actors to drive a truck through.&lt;/p&gt;&lt;p&gt;Additionally, governance models which provide a scripted path onto the decision making committee can often have this path exploited by bad actors, or by people for whom the politics are more important than the software. By implementing this system, the values of the project can easily shift in ways the leaders and contributors don’t expect or agree with.&lt;/p&gt;&lt;p&gt;The worst case can be that a contributor is ostracized due to the letter of the CoC, but not the spirit of it. Managing drama is a sensitive, subjective issue, but objective rules break hearts. Enough of this can burn out the leaders, creating a bigger power vacuum, without a plan to fill it.&lt;/p&gt;&lt;p&gt;In summary:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;For leaders&lt;/strong&gt;: Assume good faith until proven otherwise.&lt;/p&gt;&lt;p&gt;Do what you think is right. If someone is being a dickhead&lt;small&gt;&lt;sup&gt;†&lt;/sup&gt;&lt;/small&gt;, tell them to stop.  If they don’t stop, kick them out. Work with contributors you trust to elevate their role in the project so you can delegate responsibilities to them and have them act as good role models for the community. If you’re not good at moderating discussions or conflict resolution, find someone who is among your trusted advisors and ask them to exercise their skills.&lt;/p&gt;&lt;p&gt;If you need to, sketch up informal guidelines to give an approximation of your values, so that contributors know how to act and what to expect, but make it clear that they’re guidelines rather than rules. Avoid creating complex systems of governance. Especially avoid setting up systems which create paths that untrusted people can use to quickly weasel their way into positions of power. Don’t give power to people who don’t have a stake in the project.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;For contributors&lt;/strong&gt;: Assume good faith until proven otherwise.&lt;/p&gt;&lt;p&gt;Do what you think is right. If someone is being a dickhead&lt;small&gt;&lt;sup&gt;†&lt;/sup&gt;&lt;/small&gt;, talk to the leadership about it. If you don’t trust the project leadership, the project isn’t for you, and future conflicts aren’t going to go your way. Be patient with your maintainers — remember that you have the easier job.&lt;/p&gt;&lt;p&gt;&lt;small&gt;&lt;sup&gt;†&lt;/sup&gt; According to your subjective definition of dickhead.&lt;/small&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Effective-project-governance/</link>
        
        <pubDate>Fri, 17 Jan 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Effective-project-governance/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, January 2020</title>
          <description>
            &lt;p&gt;I forgot to write this post this morning, and I’m on cup 3 of coffee while knee-deep in some arcane work with tarballs in Python. Forgive the brevity of this introduction. Let’s get right into the status update.&lt;/p&gt;&lt;p&gt;First of all, &lt;a href=&quot;https://fosdem.org/2020/&quot; target=&quot;_blank&quot;&gt;FOSDEM 2020&lt;/a&gt; is taking place on February 1st and 2nd, and I’m planning on being there again this year. I hope to see you there! I’ll be hosting another &lt;a href=&quot;https://fosdem.org/2020/schedule/event/bof_sourcehut/&quot; target=&quot;_blank&quot;&gt;small session&lt;/a&gt; for SourceHut and aerc users where I’ll take questions, demo some new stuff, and give out stickers.&lt;/p&gt;&lt;p&gt;In Wayland news, the upcoming Sway 1.3 release is getting very close - rc3 is planned to ship later today. We’ve confirmed that it’ll ship with VNC support via &lt;a href=&quot;https://github.com/any1/wayvnc&quot; target=&quot;_blank&quot;&gt;wayvnc&lt;/a&gt; and improvements to input latency. I haven’t completed much extra work on Casa (and “Sway Mobile” alongside it), but there have been some small improvements. I did find some time to work on &lt;a href=&quot;https://git.sr.ht/~sircmpwn/sedna&quot; target=&quot;_blank&quot;&gt;Sedna&lt;/a&gt;, however. We’ve decided to use it as a proving grounds for the new wlroots scene graph API, which plans to incorporate Simon Ser’s &lt;a href=&quot;https://github.com/emersion/libliftoff&quot; target=&quot;_blank&quot;&gt;libliftoff&lt;/a&gt; and put to rest the eternal debate over how wlroots renderer should take shape. This’ll be &lt;em&gt;lots&lt;/em&gt; of work but the result will be a remarkably good foundation on which we can run performant compositors on a huge variety of devices — and, if we’re lucky, might help resolve the Nvidia problem. I also did a bit more work on the &lt;a href=&quot;https://wayland-book.com&quot; target=&quot;_blank&quot;&gt;Wayland Book&lt;/a&gt;, refactoring some of the chapter ordering to make more sense and getting started with the input chapter. More soon.&lt;/p&gt;&lt;p&gt;On SourceHut, lots of new developments have been underway. The latest round of performance improvements for git.sr.ht finally landed with the introduction of new server hardware, and it’s finally competitive with its peers in terms of push and web performance. I’ve also overhauled our monitoring infrastructure &lt;a href=&quot;https://metrics.sr.ht&quot; target=&quot;_blank&quot;&gt;and made it public&lt;/a&gt;. Our &lt;a href=&quot;https://sourcehut.org/blog/2020-01-13-sourcehut-q4-2019-financial-report/&quot; target=&quot;_blank&quot;&gt;Q4 2019 financial report&lt;/a&gt; was also published earlier this week. I’m currently working on pushing forward through the self-service data ownership goals, and we’ve already seen some improvements in that todo.sr.ht can now re-import tracker exports from itself or other todo.sr.ht instances.&lt;/p&gt;&lt;p&gt;I’ve also been working more on &lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;himitsu&lt;/a&gt; recently, though I’m taking it pretty slowly because it’s a security-sensitive project. Most of the crypto code has been written at this point - writing encrypted secrets to disk, reading and writing the key index - but reading encrypted secrets back from the disk remains to be implemented. I know there are some bugs in the current implementation, which I’ll be sorting out before I write much more code. I also implemented most of the support code for the Unix socket RPC, and implemented a couple of basic commands which have been helpful with proving out the secret store code (proving that it’s wrong, at least).&lt;/p&gt;&lt;p&gt;Simon Ser’s &lt;a href=&quot;https://mrsh.sh&quot; target=&quot;_blank&quot;&gt;mrsh&lt;/a&gt; has also been going very well lately, and is now a nearly complete implementation of the POSIX shell. I’ve started working on something I’ve long planned to build on top of mrsh: a comfortable interactive shell, inspired by fish’s interactive mode, but with a strictly POSIX syntax. I call the project &lt;a href=&quot;https://git.sr.ht/~sircmpwn/imrsh&quot; target=&quot;_blank&quot;&gt;imrsh&lt;/a&gt;, for interactive mrsh. I’ve already got it in somewhat good shape, but many of the features remain to be implemented. The bulk of the work was in Simon’s mrsh, so it shouldn’t be too hard to add a pretty interface on top. We’ll see how it goes.&lt;/p&gt;&lt;p&gt;That’s all for today. In the coming month I hope to expand on each of these, and I’m also working on a new Secret Project which may start bearing fruits soon (but likely not). Thank you for your continued support! I’ll see you at FOSDEM.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-January-2020/</link>
        
        <pubDate>Wed, 15 Jan 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-January-2020/</guid>
      </item>
    
      <item>
        
        
          <title>Following up on &quot;Hello world&quot;</title>
          <description>
            &lt;p&gt;This is a follow-up to my last article, &lt;a href=&quot;https://drewdevault.com/2020/01/04/Slow.html&quot; target=&quot;_blank&quot;&gt;Hello world&lt;/a&gt;, which is easily the most negatively received article I’ve written — a remarkable feat for someone who’s written as much flame bait as me. Naturally, the fault lies with the readers.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/1984/&quot; rel=&quot;noopener&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/misinterpretation_2x.png&quot; width=&quot;294&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;All jokes aside, I’ll try to state my point better. The “Hello world” article was a lot of work to put together — frustrating work — by the time I had finished collecting numbers, I was exhausted and didn’t pay much mind to putting context to them. This left a lot of it open to interpretation, and a lot of those interpretations didn’t give the benefit of the doubt.&lt;/p&gt;&lt;p&gt;First, it’s worth clarifying that the assembly program I gave is a &lt;em&gt;hypothetical, idealized&lt;/em&gt; hello world program, and in practice not even the assembly program is safe from bloat. After it’s wrapped up in an ELF, even after stripping, the binary bloats up to &lt;strong&gt;157×&lt;/strong&gt; the size of the actual machine code. I had hoped this would be more intuitively clear, but the take-away is that the ideal program is a pipe dream, not a standard to which the others are held. As the infinite frictionless plane in vacuum is to physics, that assembly program is to compilers.&lt;/p&gt;&lt;p&gt;I also made the mistake of including the runtime in the table. What I wanted you to notice about the timestamp is that it &lt;em&gt;rounds to zero&lt;/em&gt; for 15 of the 21 test cases, and arguably only one or two approach the realm of human perception. It’s meant to lend balance to the point I’m making with the number of syscalls: despite the complexity on display, the user generally can’t even tell. The other problem with including the runtimes is that it makes it look like a benchmark, which it’s not (you’ll notice that if you grep for “benchmark”, you will find no results).&lt;/p&gt;&lt;p&gt;Another improvement would have been to group rows of the table by orders of magnitude (in terms of number of syscalls), and maybe separate the outliers in each group. There is little difference between many of the languages in the middle of the table, but when one of them is your favorite language, “stacking it up” against its competitors like this is a good way to get the reader’s blood pumping and bait some flames. If your language appears to be represented unfavorably on this chart, you’re likely to point out the questionable methodology, golf your way to a more generous sample code, etc; things I could have done myself were I trying to make a benchmark rather than a point about complexity.&lt;/p&gt;&lt;p&gt;And hidden therein is my actual point: complexity. There has long been a trend in computing of endlessly piling on the abstractions, with no regard for the consequences. The web is an ever growing mess of complexity, with larger and larger blobs of inscrutable JavaScript being shoved down pipes with no regard for the pipe’s size or the bridge toll charged by the end-user’s telecom. Electron apps are so far removed from hardware that their jarring non-native UIs can take seconds to respond and eat up the better part of your RAM to merely show a text editor or chat application.&lt;/p&gt;&lt;p&gt;The PC in front of me is literally five thousand times faster than the graphing calculator in my closet - but the latter can boot to a useful system in a fraction of a millisecond, while my PC takes almost a minute. Productivity per CPU cycle per Watt is the lowest it’s been in decades, and is orders of magnitude (plural) beneath its potential. So far as most end-users are concerned, computers haven’t improved in meaningful ways in the past 10 years, and in many respects have become worse. The cause is well-known: programmers have spent the entire lifetime of our field recklessly piling abstraction on top of abstraction on top of abstraction. We’re more concerned with shoving more spyware at the problem than we are with optimization, outside of a small number of high-value problems like video decoding.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Programs have grown fat and reckless in scope, and it affects literally everything, even down to the last bastion of low-level programming: C.&lt;/p&gt;&lt;p&gt;I use syscalls as an approximation of this complexity. Even for one of the simplest possible programs, there is a huge amount of abstraction and complexity that comes with many approaches to its implementation. If I just print “hello world” in Python, users are going to bring along almost a million lines of code to run it, the fraction of which isn’t dead code is basically a rounding error. This isn’t &lt;em&gt;always&lt;/em&gt; a bad thing, but it often is and no one is thinking about it.&lt;/p&gt;&lt;p&gt;That’s the true message I wanted you to take away from my article: most programmers aren’t thinking about this complexity. Many choose tools because it’s easier for them, or because it’s what they know, or because developer time is more expensive than the user’s CPU cycles or battery life and the engineers aren’t signing the checks. I hoped that many people would be surprised at just how much work their average programming language could end up doing even when given simple tasks.&lt;/p&gt;&lt;p&gt;The point was not that your programming language is wrong, or that being higher up on the table is better, or that programming languages should be blindly optimizing these numbers. The point is, if these numbers surprised you, then you should find out why! I’m a systems programmer — I want you to be interested in your systems! And if this surprises you, I wonder what else might…&lt;/p&gt;&lt;p&gt;I know that article didn’t do a good job of explaining any of this. I’m sorry.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Now to address more specific comments:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;What the fuck is a syscall&lt;/strong&gt;?&lt;/p&gt;&lt;p&gt;This question is more common with users of the languages which make more of them, ironically. A syscall is when your program asks the kernel to do something for it. This causes a transition from &lt;em&gt;user space&lt;/em&gt; to &lt;em&gt;kernel space&lt;/em&gt;. This transition is one of the more expensive things your programs can do, but a program that doesn’t make any syscalls is not a useful program: syscalls are necessary to do any kind of I/O (input or output). &lt;a href=&quot;https://en.wikipedia.org/wiki/System_call&quot; target=&quot;_blank&quot;&gt;Wikipedia page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;On Linux, you can use the &lt;a href=&quot;https://linux.die.net/man/1/strace&quot; target=&quot;_blank&quot;&gt;strace&lt;/a&gt; tool to analyze the syscalls your programs are making, which is how I obtained the numbers in the original article.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;This “benchmark” is biased against JIT’d and interpreted languages&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Yes, it is. It &lt;em&gt;is&lt;/em&gt; true that many programming environments have to factor in a “warm up” time. This argument on its face-value is apparently validated by the cargo-culted (and often correct) wisdom that benchmarks should be conducted with timers in-situ, post warm-up period, with the measured task being repeated many times so that trends become more obvious.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; It’s precisely these details, which the conventional benchmarking wisdom aims to obscure, that I’m trying to cast a light on. While a benchmark which shows how quickly a bunch of programming languages can print “hello world” a million times&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; might be interesting, it’s not what I’m going for here.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Rust is doing important things with those syscalls&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;My opinion on this is mixed: yes, stack guards are useful. However, my “hello world” program has zero chance of causing a stack overflow. In theory, Rust should be able to reckon whether or not many programs are at risk of stack overflow.  If not, it can ask the programmer to specify some bounds, or it can emit the stack guards &lt;em&gt;only in those cases&lt;/em&gt;. The worst option is panicking, and I’m surprised that Crustaceans feel like this is sufficient. Funny, given their obsession with “zero cost” abstractions, that a nonzero-cost abstraction would be so fiercely defended. They’re already used to overlong compile times, adding more analysis probably won’t be noticed ;)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Go is doing important things with those syscalls&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;On this I wholly disagree. I hate the Go runtime, it’s the worst thing about an otherwise great language. Go programs are almost impossible to debug for having to sift through mountains of unrelated bullshit the program is doing, all to support a concurrency/parallelism model that I also strongly dislike. There are some bad design decisions in Golang and stracing the average Go program brings a lot of them to light. Illumos has many of its own problems, but &lt;a href=&quot;http://dtrace.org/blogs/wesolows/2014/12/29/golang-is-trash/&quot; target=&quot;_blank&quot;&gt;this article&lt;/a&gt; about porting Go to it covers a number of related problems.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Wow, Zig is competitive with assembly?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Yeah, I totally had the same reaction. I’m interested to see how it measures up under more typical workloads. People keep asking me what I think about Zig in general, and I think it has potential, but I also have a lot of complaints. It’s not likely to replace C for me, but it might have a place somewhere in my stack.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Re-Slow/</link>
        
        <pubDate>Wed, 08 Jan 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Re-Slow/</guid>
      </item>
    
      <item>
        
        
          <title>Hello world</title>
          <description>
            &lt;p&gt;Let’s say you ask your programming language to do the simplest possible task: print out “hello world”. Generally this takes two syscalls: write and exit. The following assembly program is the ideal Linux x86_64 program for this purpose. A perfect compiler would emit this hello world program for any language.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;bits 64
section .text
global _start
_start:
	mov rdx, len
	mov rsi, msg
	mov rdi, 1
	mov rax, 1
	syscall

	mov rdi, 0
	mov rax, 60
	syscall

section .rodata
msg: db &amp;quot;hello world&amp;quot;, 10
len: equ $-msg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Most languages do a whole lot of other crap other than printing out “hello world”, even if that’s all you asked for.&lt;/p&gt;&lt;table class=&quot;table table-bordered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Test case&lt;/th&gt;
      &lt;th&gt;Source&lt;/th&gt;
      &lt;th&gt;Execution time&lt;/th&gt;
      &lt;th&gt;Total syscalls&lt;/th&gt;
      &lt;th&gt;Unique syscalls&lt;/th&gt;
      &lt;th&gt;Size (KiB)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Assembly&lt;/strong&gt; (x86_64)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#tests&quot;&gt;test.S&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;8.6 KiB*&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Zig&lt;/strong&gt; (small)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testzig&quot;&gt;test.zig&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;10.3 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Zig&lt;/strong&gt; (safe)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testzig&quot;&gt;test.zig&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;11.3 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (musl, static)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testc&quot;&gt;test.c&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;95.9 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (musl, dynamic)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testc&quot;&gt;test.c&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;15&lt;/td&gt;
      &lt;td&gt;9&lt;/td&gt;
      &lt;td&gt;602 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (glibc, static*)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testc&quot;&gt;test.c&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;11&lt;/td&gt;
      &lt;td&gt;9&lt;/td&gt;
      &lt;td&gt;2295 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (glibc, dynamic)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testc&quot;&gt;test.c&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;65&lt;/td&gt;
      &lt;td&gt;13&lt;/td&gt;
      &lt;td&gt;2309 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Rust&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testrs&quot;&gt;test.rs&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;123&lt;/td&gt;
      &lt;td&gt;21&lt;/td&gt;
      &lt;td&gt;244 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Crystal&lt;/strong&gt; (static)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testcr&quot;&gt;test.cr&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;144&lt;/td&gt;
      &lt;td&gt;23&lt;/td&gt;
      &lt;td&gt;935 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt; (static w/o cgo)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testgo&quot;&gt;test.go&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;152&lt;/td&gt;
      &lt;td&gt;17&lt;/td&gt;
      &lt;td&gt;1661 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;D&lt;/strong&gt; (dmd)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testd&quot;&gt;test.d&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;152&lt;/td&gt;
      &lt;td&gt;20&lt;/td&gt;
      &lt;td&gt;5542 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;D&lt;/strong&gt; (ldc)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testd&quot;&gt;test.d&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;181&lt;/td&gt;
      &lt;td&gt;21&lt;/td&gt;
      &lt;td&gt;10305 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Crystal&lt;/strong&gt; (dynamic)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testcr&quot;&gt;test.cr&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;183&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
      &lt;td&gt;2601 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt; (w/cgo)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testgo&quot;&gt;test.go&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;211&lt;/td&gt;
      &lt;td&gt;22&lt;/td&gt;
      &lt;td&gt;3937 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Perl&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testpl&quot;&gt;test.pl&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.00s real&lt;/td&gt;
      &lt;td&gt;255&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
      &lt;td&gt;5640 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Java&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testjava&quot;&gt;Test.java&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.07s real&lt;/td&gt;
      &lt;td&gt;226&lt;/td&gt;
      &lt;td&gt;26&lt;/td&gt;
      &lt;td&gt;15743 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Node.js&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testjs&quot;&gt;test.js&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.04s real&lt;/td&gt;
      &lt;td&gt;673&lt;/td&gt;
      &lt;td&gt;40&lt;/td&gt;
      &lt;td&gt;36000 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Python 3&lt;/strong&gt; (PyPy)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testpy&quot;&gt;test.py&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.68s real&lt;/td&gt;
      &lt;td&gt;884&lt;/td&gt;
      &lt;td&gt;32&lt;/td&gt;
      &lt;td&gt;9909 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Julia&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testjl&quot;&gt;test.jl&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.12s real&lt;/td&gt;
      &lt;td&gt;913&lt;/td&gt;
      &lt;td&gt;41&lt;/td&gt;
      &lt;td&gt;344563 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Python 3&lt;/strong&gt; (CPython)&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testpy&quot;&gt;test.py&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.02s real&lt;/td&gt;
      &lt;td&gt;1200&lt;/td&gt;
      &lt;td&gt;33&lt;/td&gt;
      &lt;td&gt;15184 KiB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Ruby&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;
        &lt;a href=&quot;#testrb&quot;&gt;test.rb&lt;/a&gt;
      &lt;/td&gt;
      &lt;td&gt;0.04s real&lt;/td&gt;
      &lt;td&gt;1401&lt;/td&gt;
      &lt;td&gt;38&lt;/td&gt;
      &lt;td&gt;1283 KiB&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;div style=&quot;text-align: right&quot;&gt;
  &lt;small&gt;* See notes for this test case&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;This table is sorted so that the number of syscalls goes up, because I reckon more syscalls is a decent metric for how much shit is happening that you didn’t ask for (i.e. &lt;code&gt;write(&amp;quot;hello world\n&amp;quot;); exit(0)&lt;/code&gt;). Languages with a JIT fare much worse on this than compiled languages, but I have deliberately chosen not to account for this.&lt;/p&gt;&lt;p&gt;These numbers are real. This is more complexity that someone has to debug, more time your users are sitting there waiting for your program, less disk space available for files which actually matter to the user.&lt;/p&gt;&lt;h3&gt;Environment&lt;/h3&gt;&lt;p&gt;Tests were conducted on January 3rd, 2020.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;gcc 9.2.0&lt;/li&gt;&lt;li&gt;glibc 2.30&lt;/li&gt;&lt;li&gt;musl libc 1.1.24&lt;/li&gt;&lt;li&gt;Linux 5.4.7 (Arch Linux)&lt;/li&gt;&lt;li&gt;Linux 4.19.87 (vanilla, Alpine Linux) is used for musl libc tests&lt;/li&gt;&lt;li&gt;Go 1.13.5&lt;/li&gt;&lt;li&gt;Rustc 1.40.0&lt;/li&gt;&lt;li&gt;Zig 0.5.0&lt;/li&gt;&lt;li&gt;OpenJDK 11.0.5 JRE&lt;/li&gt;&lt;li&gt;Crystal 0.31.1&lt;/li&gt;&lt;li&gt;NodeJS 13.5.0&lt;/li&gt;&lt;li&gt;Julia 1.3.1&lt;/li&gt;&lt;li&gt;Python 3.8.1&lt;/li&gt;&lt;li&gt;PyPy 7.3.0&lt;/li&gt;&lt;li&gt;Ruby 2.6.4p114 (2019-10-01 rev 67812)&lt;/li&gt;&lt;li&gt;dmd 1:2.089.0&lt;/li&gt;&lt;li&gt;ldc 2:1.18.0&lt;/li&gt;&lt;li&gt;Perl 5.30.1&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For each language, I tried to write the program which would give the most generous scores without raising eyebrows at a code review. The size of all files which must be present at runtime (interpreters, stdlib, libraries, loader, etc) are included. Binaries were stripped where appropriate.&lt;/p&gt;&lt;p&gt;This was not an objective test, this is just an approximation that I hope will encourage readers to be more aware of the consequences of their abstractions, and their exponential growth as more layers are added.&lt;/p&gt;&lt;h3&gt;test.S&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;bits 64
section .text
global _start
_start:
	mov rdx, len
	mov rsi, msg
	mov rdi, 1
	mov rax, 1
	syscall

	mov rdi, 0
	mov rax, 60
	syscall

section .rodata
msg: db &amp;quot;hello world&amp;quot;, 10
len: equ $-msg
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;nasm -f elf64 test.S
gcc -o test -static -nostartfiles -nostdlib -nodefaultlibs
strip test: 8.6 KiB
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;This program only works on x86_64 Linux.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The size depends on how you measure it:&lt;/p&gt;&lt;p&gt;&lt;em&gt;Instructions + data alone&lt;/em&gt;: 52 bytes&lt;/p&gt;&lt;p&gt;&lt;em&gt;Stripped ELF&lt;/em&gt;: 8.6 KiB&lt;/p&gt;&lt;p&gt;&lt;em&gt;Manually minified ELF&lt;/em&gt;: &lt;a href=&quot;http://timelessname.com/elfbin/&quot; target=&quot;_blank&quot;&gt;142 bytes&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;test.zig&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;const std = @import(&amp;quot;std&amp;quot;);

pub fn main() !void {
    const stdout = try std.io.getStdOut();
    try stdout.write(&amp;quot;hello world\n&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;# small
zig build-exe test.zig --release-small --strip
# safe
zig build-exe test.zig --release-safe --strip
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Written with the assistance of Andrew Kelly (maintainer of Zig)&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;test.c&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;int puts(const char *s);

int main(int argc, char *argv[]) {
    puts(&amp;quot;hello world&amp;quot;);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;# dynamic
gcc -O2 -o test test.c
strip test

# static
gcc -O2 -o test -static test.c
strip test
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;glibc programs can never truly be statically linked. The size reflects this.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;test.rs&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;fn main() {
    println!(&amp;quot;hello world&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;rustc -C opt-levels=s test.rs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The final binary is dynamically linked with glibc, which is included in the size.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;test.go&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;package main

import &amp;quot;os&amp;quot;

func main() {
    os.Stdout.Write([]byte(&amp;quot;hello world\n&amp;quot;))
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;# dynamic
go build -o test test.go

# static w/o cgo
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o test -ldflags &amp;apos;-extldflags &amp;quot;-f no-PIC -static&amp;quot;&amp;apos; -buildmode pie -tags &amp;apos;osusergo netgo static_build&amp;apos; test.go
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Aside: it is getting way too goddamn difficult to build static Go binaries.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The statically linked test was run on Alpine Linux with musl libc. It doesn’t link to libc in theory, but hey.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Test.java&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;public class Test {
    public static void main(String[] args) {
        System.out.println(&amp;quot;hello world&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;javac Test.java
java Test
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;test.cr&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;puts &amp;quot;hello world\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;# Dynamic
crystal build -o test test.cr

# Static
crystal build --static -o test test.cr
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The Crystal tests were run on Alpine Linux with musl libc.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;test.js&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;console.log(&amp;quot;hello world&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;node test.js
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;test.jl&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;println(&amp;quot;hello world&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;julia test.jl
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Julia numbers were provided by a third party&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;test.py&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;print(&amp;quot;hello world&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;# cpython
python3 test.py
# pypy
pypy3 test.py
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;test.pl&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;print &amp;quot;hello world\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;perl test.pl
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Passing /dev/urandom into perl is equally likely to print “hello world”&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;test.d&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;import std.stdio;
void main()
{
    writeln(&amp;quot;hello world&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;# dmd
dmd -O test.d
# ldc
ldc -O test.d
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;test.rb&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;puts &amp;quot;hello world\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;ruby test.rb
&lt;/code&gt;&lt;/pre&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Slow/</link>
        
        <pubDate>Sat, 04 Jan 2020 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Slow/</guid>
      </item>
    
      <item>
        
        
          <title>Managing my dotfiles as a git repository</title>
          <description>
            &lt;p&gt;There are many tools for managing your dotfiles - user-specific configuration files. GNU stow is an example. I’ve tried a few solutions over the years, but I settled on a very simple system several years ago which has served me very well in the time since: my $HOME is a git repository. &lt;a href=&quot;https://git.sr.ht/~sircmpwn/dotfiles&quot; target=&quot;_blank&quot;&gt;This repository&lt;/a&gt;, in fact. This isn’t an original idea, but I’m not sure where I first heard it from either, and I’ve extended upon it somewhat since.&lt;/p&gt;&lt;p&gt;The key to making this work well is my one-byte &lt;code&gt;.gitignore&lt;/code&gt; file:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this line, and git will ignore all of the files in my $HOME directory, so I needn’t worry about leaving personal files, music, videos, other git repositories, and so on, in my public dotfiles repo. But, in order to track anything at all, we need to override the gitignore file on a case-by-case basis with &lt;code&gt;git add -f&lt;/code&gt;, or &lt;code&gt;--force&lt;/code&gt;. To add my vimrc, I used the following command:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;git add -f .vimrc
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then I can commit and push normally, and .vimrc is tracked by git. The gitignore file does not apply to any files which are already being tracked by git, so any future changes to my vimrc show up in git status, git diff, etc, and can be easilly committed with &lt;code&gt;git commit -a&lt;/code&gt;, or added to the staging area normally with &lt;code&gt;git add&lt;/code&gt; — using &lt;code&gt;-f&lt;/code&gt; is no longer necessary. Setting up a new machine is quite easy. After the installation, I run the following commands:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;cd ~
git init
git remote add origin git@git.sr.ht:~sircmpwn/dotfiles
git fetch
git checkout -f master
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A quick log-out and back in and I feel right at $HOME. Additionally, I have configured $HOME as a prefix, so that ~/bin is full of binaries, ~/lib has libraries, and so on; though I continue to use ~/.config rather than ~/etc. I put $HOME/bin ahead of anything else in my path, which allows me to shadow system programs with wrapper scripts as necessary. For example, ~/bin/xdg-open is as follows:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string embedded&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;string embedded property&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;string embedded&quot;&gt;%%:*}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;in&lt;/span&gt;
	http&lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;https&lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt;*.pdf)
		&lt;span class=&quot;constant function&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;qutebrowser&lt;/span&gt; &lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt;
		;;
	mailto)
		&lt;span class=&quot;constant function&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;aerc&lt;/span&gt; &lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt;
		;;
	*)
		&lt;span class=&quot;constant function&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/usr/bin/xdg-open&lt;/span&gt; &lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;@&amp;quot;&lt;/span&gt;
		;;
&lt;span class=&quot;keyword&quot;&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replacing the needlessly annoying-to-customize xdg-open with one that just does what I want, falling back to /usr/bin/xdg-open if necessary. Many other non-shadowed scripts and programs are found in ~/bin as well.&lt;/p&gt;&lt;p&gt;However, not all of my computers are configured equally. Some run different Linux (or non-Linux) distributions, or have different concerns being desktops, servers, laptops, phones, etc. It’s often useful for this reason to be able to customize my configuration for each host. For example, before $HOME/bin in my $PATH, I have $HOME/bin/$(hostname). I also run several machines on different architectures, so I include $HOME/bin/$(uname -m)&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; as well. To customize my sway configuration to consider the different device configurations of each host, I use the following directive in ~/.config/sway/config:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;include ~/.config/sway/`hostname`
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then I have a host-specific configuration there, also tracked by git so I can conveniently update one machine from another. I take a similar approach to per-host configuration for many other pieces of software I use.&lt;/p&gt;&lt;p&gt;Rather than using (and learning) any specialized tools, I find my needs quite adequately satisfied by a simple composition of several Unix primitives with a tool I’m already very familiar with: git. Version controlling your configuration files is a desirable trait even with other systems, so why not ditch the middleman?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/dotfiles/</link>
        
        <pubDate>Mon, 30 Dec 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/dotfiles/</guid>
      </item>
    
      <item>
        
        
          <title>PinePhone review</title>
          <description>
            &lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;: Holy shit! This is the phone I have always wanted. I have never been this excited about the mobile sector before. However: the software side is totally absent — phone calls are very dubious, SMS is somewhat dubious, LTE requires some hacks, and everything will have to be written from the ground up.&lt;/p&gt;&lt;p&gt;I have a PinePhone developer edition model, which I paid for out of pocket&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and which took an excruciatingly long time to arrive. When it finally arrived, it came with no SIM or microSD card (expected), and the eMMC had some half-assed version of Android on it which just boot looped without POSTing to anything useful&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. This didn’t bother me in the slightest — like any other computer I’ve purchased, I planned on immediately flashing my own OS on it. My Linux distribution of choice for it is &lt;a href=&quot;https://postmarketos.org/&quot; target=&quot;_blank&quot;&gt;postmarketOS&lt;/a&gt;, which is basically the mobile OS I’d build if I wanted to build a mobile OS.&lt;/p&gt;&lt;p&gt;Let me make this clear: &lt;strong&gt;right now, there are very few people, perhaps only dozens, for whom this phone is the right phone, given the current level of software support&lt;/strong&gt;. I am not using it as my daily driver, and I won’t for some time. The only kind of person I would recommend this phone to is a developer who believes in the phone and wants to help build the software necessary for it to work.  However, it seems to me that all of the right people &lt;em&gt;are&lt;/em&gt; working on the software end of this phone — everyone I’d expect from the pmOS community, from KDE, from the kernel hackers — this phone has an unprecedented level of community support and the software &lt;em&gt;will&lt;/em&gt; be written.&lt;/p&gt;&lt;p&gt;So, what’s it actually like?&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;Expand for a summary of the specs&lt;/summary&gt;
  &lt;p&gt;
    The device is about
    &lt;abbr title=&quot;The thickness of a GameBoy cartridge&quot;&gt;1 cm thick&lt;/abbr&gt;
    and weighs
    &lt;abbr
      title=&quot;The weight of one GameBoy Color, with batteries, without cartridge&quot;
    &gt;188 grams&lt;/abbr&gt;. The screen is about 16 cm tall, of which 1.5 cm is bezel,
    and &lt;abbr
      title=&quot;About the width and height of a GameBoy color, plus 1 inch of height&quot;
    &gt;7.5 cm wide&lt;/abbr&gt; (5 mm of bezel). The physical size and weight is very
    similar to my daily driver, a Samsung Galaxy J7 Refine. It has a USB-C port,
    which I understand can be reconfigured for DisplayPort, and a standard
    headphone jack and speakers, both of which sound fine in my experience. The
    screen is 720x1440, and looks about as nice as any other phone. It has
    front- and back-facing cameras, which I&apos;ve yet to get working (I understand
    that someone has got them working at some point), plus a flash/lamp on the
    back, and an &lt;abbr
      title=&quot;Note that the only values for R, G, and B that I&apos;ve managed to get working are 0.0 and 1.0 each, for a total of 7 possible colors (including off)&quot;
    &gt;RGB LED&lt;/abbr&gt; on the front.
  &lt;/p&gt;
  &lt;p&gt;
    The eMMC is 16G and, side note, had &lt;em&gt;seventeen&lt;/em&gt; partitions on it when
    I first got the phone. 2G of RAM, 4 cores. It&apos;s not very powerful, but in my
    experience it runs lightweight UIs (such as &lt;a
    href=&quot;https://swaywm.org&quot;&gt;sway&lt;/a&gt;) just fine. With very little effort by
    way of power management, and with obvious power sinks left unfixed, the
    battery lasts about 5 hours.
  &lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;In short, I’m quite satisfied with it, but I’ve never had especially strenuous demands of my phone. I haven’t run any benchmarks on the GPU, but it seems reasonably fast and the open-source Lima driver supports GLESv2. The modem is supported by &lt;a href=&quot;https://01.org/ofono&quot; target=&quot;_blank&quot;&gt;Ofono&lt;/a&gt;, which is a telephony daemon based on dbus — however, I understand that we can just open &lt;code&gt;/dev/ttyUSB1&lt;/code&gt; and talk to the modem ourselves, and I may just write a program that does this. Using Ofono, I have successfully spun up LTE internet, sent and received SMS messages, and placed and answered phone calls - though the last one without working audio. A friend from KDE, Bhushan Shah, is working on this and rumor has it that a call has successfully been placed. I have not had success with MMS, but I think it’s possible. WiFi works. All of this with zero blobs and a kernel which is… admittedly, pretty heavily patched, but &lt;a href=&quot;https://gitlab.com/pine64-org/linux&quot; target=&quot;_blank&quot;&gt;open source&lt;/a&gt; and making its way upstream.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Of course, no one wants to place phone calls by typing a lengthy command into their terminal, but that these features can be done in an annoying way means that it’s feasible to write applications that do this in a convenient way. For my part, I have been working on some components of a mobile-friendly Wayland compositor, based on Sway, which I’m calling Sway Mobile for the time being. I’m not sure if Sway will actually stick around once it becomes difficult to bend to my will (it’s designed for keyboard-driven operation, after all), but I’m building mobile shell components which will translate nicely to any other wlroots-based compositors.&lt;/p&gt;&lt;p&gt;The first of these is a simple app drawer, which I’ve dubbed &lt;a href=&quot;https://git.sr.ht/~sircmpwn/casa&quot; target=&quot;_blank&quot;&gt;casa&lt;/a&gt;. I have a lot more stuff planned:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A new bar/notification drawer/quick action thing&lt;/li&gt;&lt;li&gt;A dialer &amp; call manager, maybe integrated with gnome-contacts&lt;/li&gt;&lt;li&gt;A telephony daemon which records incoming SMS messages and pulls up the call manager for incoming phone calls. Idea: write incoming SMS messages into a Maildir.&lt;/li&gt;&lt;li&gt;A new touch-friendly Wayland lock screen&lt;/li&gt;&lt;li&gt;An on-screen keyboard program&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Here’s a video showing casa in action:&lt;/p&gt;&lt;video
  src=&quot;https://yukari.sr.ht/casa.webm?cache-break&quot;
  style=&quot;max-width: 50%; margin: 0 auto; display: block&quot;
  autoplay loop muted &gt;
  Your browser does not support webm playback. Please choose a browser which
  supports free and open standards.
&lt;/video&gt;
&lt;p&gt;The latest version has 4 columns and uses the space a bit better. Also, in the course of this work I put together the &lt;a href=&quot;https://gitlab.freedesktop.org/ddevault/fdicons&quot; target=&quot;_blank&quot;&gt;fdicons&lt;/a&gt; library, which may be useful to some.&lt;/p&gt;&lt;p&gt;I have all sorts of other small things to work on, like making audio behave better and improving power management. I intend to contribute these tools to postmarketOS upstream as a nice lightweight plug-and-play UI package you can choose from when installing pmOS, either improving their existing postmarketos-ui-sway meta-package or making something new.&lt;/p&gt;&lt;p&gt;In conclusion: I have been waiting for this phone for years and years and years. I have been hoping that someone would make a phone whose hardware was compatible with upstream Linux drivers, and could &lt;em&gt;theoretically&lt;/em&gt; be used as a daily driver if only the software were up to snuff. I wanted this because I knew that the free software community was totally capable of building the software for such a phone, if only the hardware existed. This is actually happening — all of the free software people I would hope are working on the PinePhone, are working on the PinePhone. And it’s only $150! I could buy four of them for the price of the typical smartphone! And I just might!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/PinePhone-review/</link>
        
        <pubDate>Wed, 18 Dec 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/PinePhone-review/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, December 2019</title>
          <description>
            &lt;p&gt;It’s December 15th and it still hasn’t snowed here. Why did I move to this godforsaken den of unholy heat and rain? I think I have chosen a latitude &lt;em&gt;just&lt;/em&gt; southerly enough to deprive me of the climate I yearn for. I take some comfort in the knowledge that I’m travelling home to see the family in a couple of weeks, and sure enough Colorado has been covered in snow for some time now. Anyway, none of this is relevant to my work, which is what you came here for. Let’s take a look at this past month.&lt;/p&gt;&lt;p&gt;I’ve started a couple of new projects this month, the first of which I call “&lt;a href=&quot;https://git.sr.ht/~sircmpwn/himitsu&quot; target=&quot;_blank&quot;&gt;himitsu&lt;/a&gt;”. The goal is to build a key-value store for secure information like passwords, keys, and so on. The design is inspired by Plan 9’s factotum, redesigned for Unix systems and somewhat broader in scope. One interesting goal of himitsu is the ability for programs to establish authenticated connections without ever handling your secret information - for example, your email client could ask himitsu to connect to an IMAP server, log in with your authentication details, then hand the authenticated file descriptor to the mail reader. The key-value store can also store things like the IMAP server address &amp; port, your username, and so on, meaning your mail reader could work out of the box with zero configuration. Work on this project will be slow going, as I have to use extra care to make sure that it’s secure and correct.&lt;/p&gt;&lt;p&gt;In SourceHut news, I focused mainly on two workstreams: single-sign-on and names.sr.ht, the upcoming DNS and domain registration service. The first finally fixes the problems with login across *.sr.ht, and now logging in once will log you in everywhere. Other issues with internal OAuth keys expiring have been fixed alongside these changes, and I’ve implemented a lot of improvements to the billing system as well. All of these should address some inconveniences which have been frustrating users for a while now. As for names.sr.ht, let’s just share another teaser screenshot:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/LLkW.png&quot;&gt;
&lt;figcaption&gt;Screenshot of domain contact management on names.sr.ht&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I also received my &lt;a href=&quot;https://www.pine64.org/pinephone/&quot; target=&quot;_blank&quot;&gt;PinePhone&lt;/a&gt; this week, and I’ve been terribly excited to work on it. I’ve already sent a few patches to postmarketOS upstream, and intend to write more, to get sway working well as a daily driver phone interface. “Sway Mobile” is now starting to take shape. The first of the projects for this is the development of a touch-friendly application launcher, which I’ve dubbed “&lt;a href=&quot;https://git.sr.ht/~sircmpwn/casa&quot; target=&quot;_blank&quot;&gt;casa&lt;/a&gt;”. Other projects I intend to work on for Sway Mobile include a new, touch-friendly bar and lock screen, a new on-screen keyboard program, and hopefully the development of touch bindings for the compositor itself. I’ll be writing up my plans in more detail, along with a review of the PinePhone itself, in a blog post next week.&lt;/p&gt;&lt;p&gt;In the course of this work, I also made a small library that readers may find useful for their own projects: &lt;a href=&quot;https://gitlab.freedesktop.org/ddevault/fdicons&quot; target=&quot;_blank&quot;&gt;libfdicons&lt;/a&gt;. It implements the FreeDesktop icon specification in a single small C library, which I need for Casa. In other Wayland news, I’ve made some modest progress on the book, and I plan on writing more for it soon. I apologise for letting it get somewhat sidelined while I focused on other projects. I ended up overhauling the XDG chapter somewhat, as I found it pretty weak on a later reading. I intend to write about seats (input) next, and will likely move the XDG chapter after the seat chapter so things flow better. I’ve also started a new Wayland compositor, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/sedna&quot; target=&quot;_blank&quot;&gt;sedna&lt;/a&gt;, which aims to reach a broader audience than Sway can, and I’ll be working on this as time permits.&lt;/p&gt;&lt;p&gt;Speaking of Sway, the next release (1.3) has been coming along, slowly but surely. We’re only blocked by one change now, and with the original author busy I’ve stepped up to offer what time I can implementing the last few changes. Once we get that merged, I’ll start working on the release process for Sway 1.3. Thank you for your patience &lt;img src=&quot;/img/heart.png&quot; style=&quot;height: 1rem;
display: inline&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc/refs/0.3.0&quot; target=&quot;_blank&quot;&gt;aerc 0.3.0&lt;/a&gt; was released this month, and progress on the next version has been going strong. Improvements to aerc have been almost entirely community driven, and I’ve only stepped in to write a few small patches here and there. Thanks to all of the contributors for their help! There are already quite a few changes in for 0.4.0, and more are in review now, including many bug fixes, more sophisticated email templates, contacts autocompletion, bulk email management, and more. All of this is thanks to the great community which has grown around it!&lt;/p&gt;&lt;p&gt;That’s all the updates I have for you today. I’m still touched by the support the community has given me to work on these projects. I could never be this productive without your help. Thank you.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-December-2019/</link>
        
        <pubDate>Sun, 15 Dec 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-December-2019/</guid>
      </item>
    
      <item>
        
        
          <title>Developers shouldn&apos;t distribute their own software</title>
          <description>
            &lt;p&gt;An oft-heard complaint about Linux is that software distribution often takes several forms: a Windows version, a macOS version, and… a Debian version, an Ubuntu version, a Fedora version, a CentOS version, an openSUSE version… but these complaints miss the point. The true distributable form for Linux software, and rather for Unix software in general, is a .tar.gz file containing the source code.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This article presumes that proprietary/nonfree software is irrelevant, and so should you.&lt;/p&gt;&lt;p&gt;That’s not to imply that end-users should take this tarball and run &lt;code&gt;./configure &amp;amp;&amp;amp; make &amp;amp;&amp;amp; sudo make install&lt;/code&gt; themselves. Rather, the responsibility for end-user software distribution is on the distribution itself. That’s why we call it a &lt;em&gt;distribution&lt;/em&gt;. This relationship may feel like an unnecessary middleman to the software developer who just wants to get their code into their user’s hands, but on the whole this relationship has far more benefits than drawbacks.&lt;/p&gt;&lt;p&gt;As the old complaint would suggest, there are hundreds of variants of Linux alone, not to mention the BSD flavors and any novel new OS that comes out next week. Each of these environments has its own take on how the system as a whole should be organized and operate, and it’s a fools’ errand for a single team to try and make sense of it all. More often than not, software which tries to field this responsibility itself sticks out like a sore thumb on the user’s operating system, totally flying in the face the conventions set out by the distribution.&lt;/p&gt;&lt;p&gt;Thankfully, each distro includes its own set of volunteers dedicated to this specific job: packaging software for the distribution and making sure it conforms to the norms of the target environment. This model also adds a set of checks and balances to the system, in which the distro maintainers can audit each other’s work for bugs and examine the software being packaged for anti-features like telemetry or advertisements, patching it out as necessary. These systems keep malware out of the repositories, handle distribution of updates, cryptographically verifying signatures, scaling the distribution out across many mirrors - it’s a robust system with decades of refinement.&lt;/p&gt;&lt;p&gt;The difference in trust between managed software repositories like Debian, Alpine Linux, Fedora, and so on; and unmanaged software repositories like PyPI, npm, Chrome extensions, the Google Play store, Flatpak, etc — is starkly obvious. Debian and its peers are full of quality software which integrates well into the host system and is free of malware. Unmanaged repositories, however, are &lt;a href=&quot;https://www.zdnet.com/article/two-malicious-python-libraries-removed-from-pypi/&quot; target=&quot;_blank&quot;&gt;constant sources&lt;/a&gt; for crapware and malware. I don’t trust developers to publish software with my best interests in mind, and developers shouldn’t ask for that level of trust. It’s only through a partnership with distributions that we can build a mutually trustworthy system for software distribution.&lt;/p&gt;&lt;p&gt;Some developers may complain that distros ship their software too slowly, but you shouldn’t sweat it. End-user distros ship updates reasonably quickly, and server distros ship updates at a schedule which meets the user’s needs. This inconsistent pace in release schedules among free software distributions is a feature, not a bug, and allows the system to work to the needs of its specific audience. You should use a distro that ships updates to &lt;em&gt;you&lt;/em&gt; at the pace you wish, and let your users do the same.&lt;/p&gt;&lt;p&gt;So, to developers: just don’t worry about distribution! Stick a tarball on your release page and leave the rest up to distros. And to users: install packages from your distro’s repositories, and learn how its packaging process works so you can get involved when you find a package missing. It’s not as hard as it looks, and they could use your help. For my part, I work both as a developer, packager, and end-user, publishing my software as tarballs, packaging some of it up for my &lt;a href=&quot;https://pkgs.alpinelinux.org/packages?name=&amp;branch=edge&amp;arch=x86_64&amp;maintainer=Drew+DeVault&quot; target=&quot;_blank&quot;&gt;distro of choice&lt;/a&gt;, report bugs to other maintainers, and field requests from maintainers of other distros as necessary. Software distribution is a social system and it works.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Developers-shouldnt-distribute/</link>
        
        <pubDate>Mon, 09 Dec 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Developers-shouldnt-distribute/</guid>
      </item>
    
      <item>
        
        
          <title>Take action to save .org and prosecute those who sold out the internet</title>
          <description>
            &lt;p&gt;As many of you have no doubt heard, control of the .org registry has been sold to private interests. There have been attempts to call them to reason, like &lt;a href=&quot;https://savedotorg.org/&quot; target=&quot;_blank&quot;&gt;Save .ORG&lt;/a&gt;, but let’s be realistic: they knew what they’re doing is wrong, the whole time. If they were a commercial entity, our appeals would fall on deaf ears and that would be the end of it. But, they’re not a commercial entity - so our appeals may fall on deaf ears, but that doesn’t have to be the end of it.&lt;/p&gt;&lt;p&gt;The level of corruption on display by the three organizations involved in this scam: ICANN (Internet Corporation for Assigned Names and Numbers), ISOC (The Internet Society), and PIR (Public Interest Registry), is astounding and very illegal. If you are not familiar with the matter, click this to read a summary:&lt;/p&gt;&lt;details&gt;
  &lt;summary&gt;Summary of the corrupt privatization of .org&lt;/summary&gt;

  &lt;p&gt;
    The governance of names on the internet is kind of complicated. ISOC
    oversees a lot of activities in internet standards and governance, but their
    role in this mess is as the parent company of PIR. PIR is responsible for
    the .org registry, which oversees the governance of .org directly and
    collects fees for every sale of a .org domain. ICANN is the broader
    authority which oversees all domain allocation on the internet, and also
    collects a fee for every domain sold. There&apos;s a complex web of documents and
    procedures which govern these three organizations, and the name system as a
    whole, and all three of them were involved in this process. Each of these
    organizations is a non-profit, except for PIR, which in the course of this
    deal is trying to convert to a B corp.
  &lt;/p&gt;

  &lt;p&gt;
    ICANN can set price limits on the sale of .org domains. In March of 2019,
    they proposed removing these price caps entirely. During the period for
    public comment, they received 3,300 comments against, and 6 in favor. On May
    13, they removed these price caps anyway.
  &lt;/p&gt;

  &lt;p&gt;
    In November 2019, ISOC announced that they had approved the sale of PIR, the
    organization responsible for .org, to Ethos Capital, for an unspecified
    amount. According to
    &lt;a
      href=&quot;https://www.internetsociety.org/board-of-trustees/minutes/147&quot;
      rel=&quot;nofollow noopener&quot;
    &gt;the minutes&lt;/a&gt;, the decision to approve this sale was unanimously voted on
    by the board. Additionally, it seems that Goldman Sachs had been involved in
    the sale to some degree.
  &lt;/p&gt;

  &lt;p&gt;
    Fadi Chehadé became the CEO of ICANN in 2012. In 2016, he leaves his
    position before it expires to start a consulting company, and he later joins
    Abry Partners. One of the 3 partners is Erik Brooks. They later acquire
    Donuts, a private company managing domains. Donuts co-founder Jon Nevett
    becomes the CEO of PIR in December 2018. On May 7th, Chehadé registers
    EthosCapital.com, and on May 13th ICANN decided to remove the price caps
    despite 0.2% support from the public. On May 14th, the following day, Ethos
    Capital was incorporated, with Brooks as the CEO. In November 2019, ISOC
    approved the acquisition of PIR by Ethos Capital, a for-profit company.
  &lt;/p&gt;

  &lt;p&gt;
    These are the names of the criminals who sold the internet. If you want to
    read more, &lt;a
      href=&quot;https://www.privateinternetaccess.com/blog/2019/11/isoc-pir-ethos-capital-deal-timeline/&quot;
      rel=&quot;noopener&quot;
    &gt;Private Internet Access&lt;/a&gt; has a good write-up.
  &lt;/p&gt;

  &lt;p&gt;Okay, now let&apos;s talk about what you can do about it.&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;If you are familiar with the .org heist, then like me, you’re probably pissed off. Here’s how you can take action: all of these organizations are 501c3 non-profits. The sale of a non-profit to a for-profit entity like this is illegal without very specific conditions being met. Additionally, this kind of behavior is not the sort the IRS likes to see in a tax-exempt organization. Therefore, we can take the following steps to put a stop to this:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Write to the CA and VA attorney general offices encouraging them to investigate the misbehavior of these three non-profits, which are incorporated in their respective states.&lt;/li&gt;&lt;li&gt;File form 13909 with the IRS, encouraging them to review the organization’s non-profit status.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This kind of behavior is illegal. The sale of a non-profit requires a letter from the Attorneys General in both California (ICANN) and Virginia (ISOC, PIR). Additionally, much of this behavior qualifies as “self-dealing”, or leveraging one’s power within an organization for their own benefit, rather than the benefit of the organization. To report this, I’ve prepared a letter to the CA and VA Attorney’s General offices, which you can read here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://yukari.sr.ht/ag-letter.pdf&quot; target=&quot;_blank&quot;&gt;Letter to the Attorney General&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I encourage you to consider writing a letter of your own, but I would not recommend copying and pasting this letter. However, this kind of behavior is also illegal in the eyes of the IRS, and a form is provided for this purpose. Form 13909 is the appropriate means for reporting this behavior. You can download a pre-filled form here, and I do encourage you to submit one of this yourself:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://yukari.sr.ht/dotorg-form-13909.pdf&quot; target=&quot;_blank&quot;&gt;Form 13909 for ICANN and ISOC complaints (PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://yukari.sr.ht/dotorg-form-13909.odg&quot; target=&quot;_blank&quot;&gt;Form 13909 for ICANN and ISOC complaints (ODG)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This only includes complaints for ICANN and ISOC, as PIR is seeking to lose its non-profit status anyway. You can print out the PDF, fill in your details on both pages, and mail it to the address printed on the form; or you can download the ODG, open it up with LibreOffice Draw, and fill in the remaining details digitally, then email it to the address shown on the page.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Happy Thanksgiving! Funny how this all happened right when the American public would be distracted…&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/dotorg/</link>
        
        <pubDate>Fri, 29 Nov 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/dotorg/</guid>
      </item>
    
      <item>
        
        
          <title>Software developers should avoid traumatic changes</title>
          <description>
            &lt;p&gt;A lot of software has gone through changes which, in retrospect, I would describe as “traumatic” to their communities. I recognize these sorts of changes by their effect: we might have pulled through in the end, but only after a lot of heartbreak, struggle, and hours of wasted hacking; but the change left a scar on the community.&lt;/p&gt;&lt;p&gt;There are two common cases in which a change risks introducing this kind of trauma:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;It requires everyone in the community, or nearly everyone, to overhaul their code to get it &lt;strong&gt;working&lt;/strong&gt; again&lt;/li&gt;&lt;li&gt;It requires everyone in the community, or nearly everyone, to overhaul their code to get it &lt;strong&gt;idiomatic&lt;/strong&gt; again&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Let’s call these cases, respectively, strong and weak trauma. While these are both traumatic changes, the kind of trauma they inflict on the community is different. The first kind is more severe, but the latter is a bad idea, too. We can examine these through two case-studies in Python: the (in)famous transition to Python 3, and the less notorious introduction of asyncio.&lt;/p&gt;&lt;p&gt;In less than one month, Python 2 will reach its end of life, and even as a staunch advocate of Python 3, I too have some software which is not going to make it to the finish line in time&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. There’s no doubt that Python 3 is much, much better than Python 2. However, the transition was poorly handled, and upgrading can be no small task for some projects. The result has been hugely divisive and intimately familiar to anyone who works with Python, creating massive rifts in the community and wasting millions of hours of engineer time addressing. This kind of “strong” trauma is fairly easy to spot in advance.&lt;/p&gt;&lt;p&gt;The weaker kind of traumatic change is more subtle, and less talked about. It’s a slow burn, and it takes a long time for its issues to manifest. Consider the case of asyncio: clearly it’s an improvement for Python, whose previous attempts at concurrency have fallen completely flat. The introduction of async/await and coroutines throughout the software ecosystem is something I’m generally very pleased about. You’ll see me reach for threads to solve a problem when hell freezes over, and no earlier, so I’m quite fond of first-class coroutines.&lt;/p&gt;&lt;p&gt;Unfortunately, this has a chilling effect on existing Python code.  The introduction of asyncio has made large amounts of code idiomatically obsolete. Requests, the darling of the Python world, is effectively useless in a theoretical idiomatic post-asyncio world. The same is true of Flask, SQLAlchemy, and many, many other projects. Just about anything that does I/O is unidiomatic now.&lt;/p&gt;&lt;p&gt;Since nothing has actually &lt;em&gt;broken&lt;/em&gt; with this change, the effects are more subtle than with strong traumatic changes. The effect of asyncio has been to hasten the onset of code rot. Almost all of SourceHut’s code pre-dates asyncio, for example, and I’m starting to feel the limitations of the pre-asyncio model. The opportunity to solve this problem by rewriting with asyncio in mind, however, also presents me a chance to rewrite in anything else, and reevaluate my choice of Python for the project entirely. It’s a tough decision to think about — the mature and diverse ecosystem of libraries that help to make a case for Python is dramatically reduced when asyncio support is a consideration.&lt;/p&gt;&lt;p&gt;It may take years for the trauma to fully manifest, but the rift is still there and can only grow. Large amounts of code is rotting and will have to be thrown away for the brave new asyncio world. The introduction of asyncio has made another clear “before” and “after” in the Python ecosystem. The years in between will be rough, because all new Python code will either leverage the rotting pre-asyncio ecosystem or suffer through an immature post-asyncio ecosystem. It’ll likely turn out for the better — years from now.&lt;/p&gt;&lt;p&gt;And sometimes these changes &lt;em&gt;are&lt;/em&gt; for the better, but they should be carefully thought out, and designed to minimize the potential impact. In practical terms, it’s for this reason that I urge caution with ideas like adding generics to Go. In a post-generics world, a large amount of the Go ecosystem will suddenly become unidiomatic, and breaking changes will required to bring it up to spec. Let’s think carefully about it, eh?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Avoid-traumatic-changes/</link>
        
        <pubDate>Tue, 26 Nov 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Avoid-traumatic-changes/</guid>
      </item>
    
      <item>
        
        
          <title>China</title>
          <description>
            &lt;p&gt;This article will be difficult to read and was difficult to write. I hope that you can stomach the uncomfortable nature of this topic and read my thoughts in earnest.  I usually focus on technology-related content, but at the end of the day, this is my personal blog and I feel that it would betray my personal principles to remain silent. I’ve made an effort to provide citations for all of my assertions.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: if you are interested in conducting an independent review of the factuality of the claims expressed in this article, please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;contact me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The keyboard I’m typing these words into bears “Made in China” on the bottom. The same is true of the monitor I’m using to edit the article. It’s not true of all of my electronics — the graphics processing unit which is driving the monitor was made in Taiwan&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and my phone was made in Vietnam.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; Regardless, there’s no doubt that my life would be, to some degree, worse off if not for trade with China. Despite this, I am prepared to accept the consequences of severing economic relations with China.&lt;/p&gt;&lt;p&gt;How bad would being cut-off from China’s economy be? We’re a net importer from China, and by over 4 times the volume.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Let’s assume, in the worst case, trade ties were completely severed. The United States would be unable to buy $155B worth of electronics, which we already have domestic manufacturing capabilities for&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; and which have a productive life of several years. We could definitely stand to get used to repairing and reusing these instead of throwing them out. We’d lose $34B in mattresses and furniture — same story. The bulk of our imports from China are luxury goods that we can already make here at home&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; — it’s just cheaper to buy them from China. But cheaper for whom?&lt;/p&gt;&lt;p&gt;This gets at the heart of the reason why we’re tied to China economically. It’s economically productive &lt;em&gt;for the 1%&lt;/em&gt; to maintain a trade relationship with China. The financial incentives don’t help any Americans, and in fact, most of us are hurt by this relationship.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; Trade is what keeps us shackled to the Chinese Communist Party government, but it’s not beneficial to anyone but those who are already obscenely rich, and certainly not for our poorest — who, going into 2020, are as likely to be high school dropouts as they are to be doctors.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fn-7-ref-1&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;So, we can cut off China. Why should we? Let’s lay out the facts: China is conducting human rights violations on the largest scale the world has seen since Nazi Germany. China executes political prisoners&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-8&quot; id=&quot;fn-8-ref-1&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; and harvests their organs for transplant to sick elites on an industrial scale, targeting and killing civilians based on not only political, but also ethnic and religious factors. This is commonly known as genocide. China denies using the organs of prisoners, but there’s credible doubt&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-9&quot; id=&quot;fn-9-ref-1&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; from the scientific community.&lt;/p&gt;&lt;p&gt;Recent evidence directly connecting executions to organ harvesting is somewhat unreliable, but I don’t think China deserves the benefit of the doubt. China is a world leader in executions, and is believed to conduct more executions than the rest of the world combined.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-10&quot; id=&quot;fn-10-ref-1&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; Wait times for organ transplantation are extraordinarily low in China,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-11&quot; id=&quot;fn-11-ref-1&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; on the order of weeks — in most of the developed world these timeframes are measured in terms of years,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-12&quot; id=&quot;fn-12-ref-1&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; and China has been unable to explain the source for tens of thousands of transplants in the past&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-13&quot; id=&quot;fn-13-ref-1&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;. And, looking past recent evidence, China directly admitted to using the organs of executed prisoners in 2005.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-14&quot; id=&quot;fn-14-ref-1&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;These atrocities are being committed against cultural minorities to further China’s power. The UN published a statement in August 2018 stating that they have credible reports of over a million ethnic Uighurs being held in internment camps in Xinjiang,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-15&quot; id=&quot;fn-15-ref-1&quot;&gt;15&lt;/a&gt;&lt;/sup&gt; imprisoned with various other ethnic minorities from the region. Leaks in November 2019 reported by the New York Times showed that China admits the imprisoned have committed no crimes other than dissent,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-16&quot; id=&quot;fn-16-ref-1&quot;&gt;16&lt;/a&gt;&lt;/sup&gt; and that the camps were to be run with, quote, “absolutely no mercy”.&lt;/p&gt;&lt;p&gt;It’s nice to believe that we would have stood up to Nazi Germany if we had been there in the 1940’s. China is our generation’s chance to prove ourselves of that conviction. We talk a big game about fighting against white nationalists in our own country, and pride ourselves on standing up against “fascists”. It’s time we turned attention to the real fascists, on the world stage.&lt;/p&gt;&lt;p&gt;Instead, the staunch capitalism of America, and the West as a whole, has swooped in to leverage Chinese fascism for a profit. Marriott Hotels apologized for listing Hong Kong, Macau, and Taiwan as countries separate from China.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-17&quot; id=&quot;fn-17-ref-1&quot;&gt;17&lt;/a&gt;&lt;/sup&gt; Apple removed the Taiwanese flag from iOS in China and the territories it claims.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-18&quot; id=&quot;fn-18-ref-1&quot;&gt;18&lt;/a&gt;&lt;/sup&gt; Activision/Blizzard banned several players for making pro-Hong Kong statements in tournaments and online.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-19&quot; id=&quot;fn-19-ref-1&quot;&gt;19&lt;/a&gt;&lt;/sup&gt; These behaviors make me ashamed to be an American.&lt;/p&gt;&lt;p&gt;Fuck that.&lt;/p&gt;&lt;p&gt;A brief history lesson: Hong Kong was originally controlled by the United Kingdom at the end of the Opium Wars. It’s beyond the scope of this article, but it’ll suffice to say that the United Kingdom was brutal and out of line, and the end result is that Hong Kong became a British colony. Because of this, it was protected from direct Chinese influence during China’s turbulent years following, and they were insulated from the effects of the Great Leap Forward and the Cultural Revolution, which together claimed tens of millions of lives and secured the Communist Party of China’s power into the present.&lt;/p&gt;&lt;p&gt;On July 1st, 1997, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Sino-British_Joint_Declaration&quot; target=&quot;_blank&quot;&gt;Sino-British Joint Declaration&lt;/a&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=k7YzJzq1Mvk&quot; target=&quot;_blank&quot;&gt;went into effect&lt;/a&gt;, and Hong Kong was turned over to China. The agreement stipulated that Hong Kong would remain effectively autonomous and self-governing for a period of 50 years — until 2047. China has been gradually and illegally eroding that autonomy ever since. Today, Hong Kong citizens have effectively no representation in their government. The Legislative Council of Hong Kong has been deliberately engineered by China to be pro-Beijing — a majority of the council is selected through processes with an inherent pro-Beijing bias, giving Hong Kong effectively no autonomous power to pass laws.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-20&quot; id=&quot;fn-20-ref-1&quot;&gt;20&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Hong Kong’s executive branch is even worse. The Chief Executive of Hong Kong (Carrie Lam) is elected by a committee of 1,200 members largely controlled by pro-Beijing seats, from a pool of pro-Beijing candidates, and the people have effectively no representation in the election. The office has been held by pro-Beijing politicians since it was established.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-21&quot; id=&quot;fn-21-ref-1&quot;&gt;21&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The ongoing protests in Hong Kong were sparked by a mainland attempt to rein in Hong Kong’s judicial system in a similar manner, with the introduction of the “Fugitive Offenders and Mutual Legal Assistance in Criminal Matters Legislation (Amendment) Bill 2019”,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-22&quot; id=&quot;fn-22-ref-1&quot;&gt;22&lt;/a&gt;&lt;/sup&gt; which would have allowed the authorities to extradite suspects awaiting trial to mainland China. These protests inspired the Hong Kong people to stand up against all of the injustices they have faced from China’s illegal encroachments on their politics. The protesters have five demands:&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-23&quot; id=&quot;fn-23-ref-1&quot;&gt;23&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Complete withdrawal of the extradition bill&lt;/li&gt;&lt;li&gt;No prosecution of the protesters&lt;/li&gt;&lt;li&gt;Retraction of the characterization of the protests as “riots”&lt;/li&gt;&lt;li&gt;Establish an independent inquiry into police misconduct&lt;/li&gt;&lt;li&gt;Resignation of Carrie Lam and the implementation of universal suffrage&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Their first demand has been met, but the others are equally important and the protests show no signs of slowing. Unfortunately, China shows no signs of slowing their crackdown either, and have been consistently escalating the matter. The police are now threatening to use live rounds on the protesters,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-24&quot; id=&quot;fn-24-ref-1&quot;&gt;24&lt;/a&gt;&lt;/sup&gt; and people are already being shot in the streets.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-25&quot; id=&quot;fn-25-ref-1&quot;&gt;25&lt;/a&gt;&lt;/sup&gt; China is going to kill the protesters, [again][tiananmen].&lt;/p&gt;&lt;p&gt;The third demand — the retraction of the characterization of the demonstrations as “riots” — and the government’s refusal to meet it, conveys a lot about China’s true intentions. Chinese law defines rioting as a capital offense,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-26&quot; id=&quot;fn-26-ref-1&quot;&gt;26&lt;/a&gt;&lt;/sup&gt; and we’ve already demonstrated their willingness to execute political prisoners on a massive scale. These protesters are going to be killed if their demands aren’t met.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-27&quot; id=&quot;fn-27-ref-1&quot;&gt;27&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Hong Kong is the place where humanity makes its stand against oppressors. The people of Hong Kong have been constant allies to the West, and their liberty is at stake. If we want others to stand up for us when our liberties are on the line, then it’s our turn to pay it forward now. The founding document of the United States of America&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-28&quot; id=&quot;fn-28-ref-1&quot;&gt;28&lt;/a&gt;&lt;/sup&gt; describes the rights they’re defending as “unalienable” — endowed upon all people by their Creator. The people of Hong Kong are our friends and we’re watching them get killed for rights that we hold dear in our own nation’s founding principles.&lt;/p&gt;&lt;p&gt;We have a legal basis for demanding these rights for Hong Kong’s people — China is blatantly violating their autonomy, which they agreed to uphold in 1984. The United Kingdom should feel obligated to step in, but they’ll need the support of the international community, which we need to be prepared to give them. We need to make an ultimatum: if China uses deadly force in Hong Kong, the international community will respond in kind.&lt;/p&gt;&lt;p&gt;China isn’t the only perpetrator of genocide today, but they are persecuting our friends. China has the second highest GDP&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-29&quot; id=&quot;fn-29-ref-1&quot;&gt;29&lt;/a&gt;&lt;/sup&gt; in the world, and somehow this makes it okay. If we won’t stand up to them, then who will? I call for a worldwide boycott of Chinese products, and of companies who kowtow to their demands or accept investment from China. I call for international condemnation of the Communist Party of China’s behavior and premise for governance. And I call for an ultimatum to protect our allies from slaughter.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/China/</link>
        
        <pubDate>Wed, 20 Nov 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/China/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, November 2019</title>
          <description>
            &lt;p&gt;Today’s update is especially exciting, because today marks the 1 year anniversary of Sourcehut &lt;a href=&quot;https://drewdevault.com/2018/11/15/sr.ht-general-availability.html&quot; target=&quot;_blank&quot;&gt;opening it’s alpha&lt;/a&gt; to public registration. I wrote a &lt;a href=&quot;https://sourcehut.org/blog/2019-11-15-sourcehut-1-year-alpha/&quot; target=&quot;_blank&quot;&gt;nice long article&lt;/a&gt; which goes into detail about what Sourcehut accomplished in 2019, what’s to come for 2020, and it lays out the entire master plan for your consideration. Be sure to give that a look if you have the time. I haven’t slowed down on my other projects, though, so here’re some more updates!&lt;/p&gt;&lt;p&gt;I’ve been pushing hard on the VR work this month, with lots of help from Simon Ser. We’ve put together &lt;a href=&quot;https://git.sr.ht/~sircmpwn/wxrc&quot; target=&quot;_blank&quot;&gt;wxrc&lt;/a&gt; - Wayland XR Compositor - which does what it says on the tin. It’s similar to what you’ve seen in my earlier updates, but it’s a bespoke C project instead of a Godot-based compositor, resulting in something much lighter weight and more efficient. The other advantage is that it’s based on OpenXR, thanks to &lt;a href=&quot;https://gitlab.freedesktop.org/monado/monado/merge_requests?scope=all&amp;utf8=%E2%9C%93&amp;state=all&amp;author_username=ddevault&quot; target=&quot;_blank&quot;&gt;our many&lt;/a&gt; &lt;a href=&quot;https://gitlab.freedesktop.org/monado/monado/merge_requests?scope=all&amp;utf8=%E2%9C%93&amp;state=all&amp;author_username=emersion&quot; target=&quot;_blank&quot;&gt;contributions&lt;/a&gt; to Monado, an open-source OpenXR runtime - the previous incarnations were based on SteamVR, which is a proprietary runtime and proprietary API. We’ve also got 3D Wayland clients working as of this week, check out our video:&lt;/p&gt;&lt;video src=&quot;https://yukari.sr.ht/wxrc-demo3.webm&quot; muted autoplay loop&gt;
  Your web browser does not support the webm video codec. Please consider using
  web browsers that support free and open standards.
&lt;/video&gt;
&lt;p&gt;This work has generated more patches for a large variety of projects - Mesa, Wayland, Xorg, wlroots, sway, new Vulkan and OpenXR standards, and more. This is really cross-cutting work and we’re making improvements across the whole graphics ecosystem to support it.&lt;/p&gt;&lt;p&gt;Speaking of Wayland, the upcoming Sway release is looking like it’s going to be really good. I mentioned this last month, but we’re still on track for getting lots of great features in - VNC support, foreign toplevel management (taskbars), input latency reductions, drawing tablet support, and more. I’m pretty excited. I wrote chapters 9 and 9.1 for the Wayland book this month as well.&lt;/p&gt;&lt;p&gt;In aerc news, thanks entirely to its contributors and not to me, lots of new features have been making their way in. Message templates are one of them, which you can take advantage of to customize the reply and forwarded message templates, or make new templates of your own. aerc has learned AUTH LOGIN support as well, and received a number of bugfixes. ctools has also seen a number of patches coming in, including support for echo, tee, and nohup, along with several bug fixes.&lt;/p&gt;&lt;p&gt;In totally off-the-wall news, I’ve &lt;a href=&quot;https://drewdevault.com/japanese/&quot;&gt;started a page&lt;/a&gt; cataloguing my tools and recommendations for Japanese language learners.&lt;/p&gt;&lt;p&gt;That’s all I’ve got for you today, I hope it’s enough! Thank you for your continued love and support, I’m really proud to be able to work on these projects for you.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-November-2019/</link>
        
        <pubDate>Fri, 15 Nov 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-November-2019/</guid>
      </item>
    
      <item>
        
        
          <title>An old-school shell hack on a line printer</title>
          <description>
            &lt;p&gt;It’s been too long since I last did a good hack, for no practical reason other than great hack value. In my case, these &lt;a href=&quot;https://drewdevault.com/2016/03/22/Integrating-a-VT220-into-my-life.html&quot; target=&quot;_blank&quot;&gt;often amount&lt;/a&gt; to a nostalgia for an age of computing I wasn’t present for. In a recent bid to capture more of this nostalgia, I recently picked up a dot matrix line printer, specifically the Epson LX-350 printer. This one is nice because it has a USB port, so I don’t have to break out my pile of serial cable hacks to get it talking to Linux 😁&lt;/p&gt;&lt;p&gt;This is the classic printer style, with infinite paper and a lovely noise during printing. They are also fairly simple to operate - you can just write text directly to &lt;code&gt;/dev/lp&lt;/code&gt; (or &lt;code&gt;/dev/usb/lp9&lt;/code&gt; in my case) and it’ll print it out. Slightly more sophisticated instructions can be written to them with ANSI escape sequences, just like a terminal. They can also be rigged up to CUPS, then you can use something like &lt;code&gt;man -t 5 scdoc&lt;/code&gt; to produce printouts like this:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/gHCA.jpg&quot;&gt;&lt;/p&gt;&lt;p&gt;Plugging the printer into Linux and writing out pages isn’t much for hack value, however. What I really wanted to make was something resembling an old-school TTY - teletypewriter. So I wrote some &lt;a href=&quot;https://git.sr.ht/~sircmpwn/lpsh&quot; target=&quot;_blank&quot;&gt;glue code in Golang&lt;/a&gt;, and soon enough I had a shell:&lt;/p&gt;&lt;iframe width=&quot;560&quot; height=&quot;315&quot; sandbox=&quot;allow-same-origin allow-scripts
allow-popups&quot;
src=&quot;https://spacepub.space/videos/embed/d8943b2d-8280-497b-85ec-bc282ec2afdc&quot;
frameborder=&quot;0&quot; allowfullscreen style=&quot;width: 100%&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;The glue code I wrote for this is fairly straightforward. In the simplest form, it spins up a pty (pseudo-terminal), runs &lt;code&gt;/bin/sh&lt;/code&gt; in it, and writes the pty output into the line printer device. For those unaware, a pseudo-terminal is the key piece of software infrastructure for running interactive text applications. Applications which want to do things like print colored text, move the cursor around and draw a TUI, and so on, will open &lt;code&gt;/dev/tty&lt;/code&gt; to open the current TTY device. For most applications used today, this is a “pseudo-terminal”, or pty, which is a terminal emulated in userspace - i.e. by your terminal emulator. However, your terminal emulator is &lt;em&gt;emulating&lt;/em&gt; a terminal - the control sequences applications send to these are backwards-compatible with 50 years of computing history.  Interfaces like these are the namesake of the TTY.&lt;/p&gt;&lt;p&gt;Visual terminals came onto the scene later on, and in the classic computing tradition, the old hands complained that it was less useful - you could no longer write notes on your backlog, tear off a page and hand it to a colleague, or &lt;a href=&quot;https://en.wikipedia.org/wiki/Wite-Out&quot; target=&quot;_blank&quot;&gt;white-out&lt;/a&gt; mistakes. Early &lt;a href=&quot;https://en.wikipedia.org/wiki/Computer_terminal&quot; target=&quot;_blank&quot;&gt;visual terminals&lt;/a&gt; could also be plugged directly into a line printer, and you could configure them to echo to the printer or print out a screenfull of text at a time. A distinct advantage of visual terminals is not having to deal with so much bloody paper, a problem that I’ve become acutely familiar with in the past few days&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;Getting back to the glue code, I chose Golang because setting up a TTY is a bit of a hassle in C, but in Golang it’s pretty straightforward. There is a serial port and in theory I could have plugged it in and spawned a getty on the resulting serial device - but (1) it’d be write-only, so not especially interactive without &lt;em&gt;hardware&lt;/em&gt; hacks, and (2) I didn’t feel like digging out my serial cables. So:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;go&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;git.sr.ht/~sircmpwn/pty&amp;quot;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// fork of github.com/kr/pty&lt;/span&gt;

&lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;variable&quot;&gt;winsize&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; pty.&lt;span class=&quot;type&quot;&gt;Winsize&lt;/span&gt;{
  &lt;span class=&quot;variable&quot;&gt;Cols&lt;/span&gt;: &lt;span class=&quot;number&quot;&gt;160&lt;/span&gt;,
  &lt;span class=&quot;variable&quot;&gt;Rows&lt;/span&gt;: &lt;span class=&quot;number&quot;&gt;24&lt;/span&gt;,
}
&lt;span class=&quot;variable&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;exec&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Command&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;/bin/sh&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;variable&quot;&gt;cmd&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;Env&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;append&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Environ&lt;/span&gt;(),
  &lt;span class=&quot;string&quot;&gt;&amp;quot;TERM=lp&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;variable&quot;&gt;fmt&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Sprintf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;COLUMNS=%d&amp;quot;&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;180&lt;/span&gt;))
&lt;span class=&quot;variable&quot;&gt;tty&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;pty&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;StartWithSize&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;cmd&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;winsize&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;P.S. We’re going to dive through the code in detail now. If you just want more cool videos of this in action, skip to the bottom.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;I set the TERM environment variable to &lt;code&gt;lp&lt;/code&gt;, for line printer, which doesn’t really exist but prevents most applications from trying anything too tricksy with their escape codes. The &lt;code&gt;tty&lt;/code&gt; variable here is an &lt;code&gt;io.ReadWriter&lt;/code&gt; whose output is sent to the printer and whose input is sourced from wherever, in my case from the stdin of this process&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;For a little more quality-of-life, I looked up Epson’s proprietary ANSI escape sequences and found out that you can tell the printer to feed back and forth in 216th” increments with the j and J escape sequences. The following code will feed 2.5” out, then back in:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;go&quot;&gt;&lt;span class=&quot;variable&quot;&gt;f&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Write&lt;/span&gt;([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xD8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xD8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x6C&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;))
&lt;span class=&quot;variable&quot;&gt;f&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Write&lt;/span&gt;([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xD8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xD8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x6C&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which happens to be the perfect amount to move the last-written line up out of the printer for the user to read, then back in to be written to some more. A little bit of timing logic in a goroutine manages the transition between “spool out so the user can read the output” and “spool in to write some more output”:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;go&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;variable function&quot;&gt;lpmgr&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;chan&lt;/span&gt; (&lt;span class=&quot;keyword&quot;&gt;interface&lt;/span&gt;{}), &lt;span class=&quot;variable&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;chan&lt;/span&gt; ([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;)) {
	&lt;span class=&quot;comment&quot;&gt;// TODO: Runtime configurable option? Discover printers? dunno&lt;/span&gt;
	&lt;span class=&quot;variable&quot;&gt;f&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;OpenFile&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;/dev/usb/lp9&amp;quot;&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;O_RDWR&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0755&lt;/span&gt;)
	&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
		&lt;span class=&quot;function_builtin variable function&quot;&gt;panic&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt;)
	}

	&lt;span class=&quot;variable&quot;&gt;feed&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;false&lt;/span&gt;
	&lt;span class=&quot;variable&quot;&gt;f&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Write&lt;/span&gt;([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;))

	&lt;span class=&quot;variable&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;250&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;time&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;Millisecond&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; {
		&lt;span class=&quot;keyword&quot;&gt;select&lt;/span&gt; {
		&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;in&lt;/span&gt;:
			&lt;span class=&quot;comment&quot;&gt;// Increase the timeout after input&lt;/span&gt;
			&lt;span class=&quot;variable&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;time&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;Second&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;out&lt;/span&gt;:
			&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;feed&lt;/span&gt; {
				&lt;span class=&quot;variable&quot;&gt;f&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Write&lt;/span&gt;([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xD8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xD8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x6C&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;))
				&lt;span class=&quot;variable&quot;&gt;feed&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;false&lt;/span&gt;
			}
			&lt;span class=&quot;variable&quot;&gt;f&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Write&lt;/span&gt;(&lt;span class=&quot;function_builtin variable function&quot;&gt;lptl&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;data&lt;/span&gt;))
		&lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;time&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;After&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;timeout&lt;/span&gt;):
			&lt;span class=&quot;variable&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;time&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;Millisecond&lt;/span&gt;
			&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;feed&lt;/span&gt; {
				&lt;span class=&quot;variable&quot;&gt;feed&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;true&lt;/span&gt;
				&lt;span class=&quot;variable&quot;&gt;f&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Write&lt;/span&gt;([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xD8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xD8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x6C&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;))
			}
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;lptl&lt;/code&gt; is a work-in-progress thing which tweaks the outgoing data for some quality-of-life changes, like changing backspace to ^H. Then, the main event loop looks something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;go&quot;&gt;&lt;span class=&quot;variable&quot;&gt;inch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;make&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;chan&lt;/span&gt; (&lt;span class=&quot;keyword&quot;&gt;interface&lt;/span&gt;{}))
&lt;span class=&quot;variable&quot;&gt;outch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;make&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;chan&lt;/span&gt; ([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;))
&lt;span class=&quot;keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;lpmgr&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;inch&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;outch&lt;/span&gt;)

&lt;span class=&quot;variable&quot;&gt;inbuf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;make&lt;/span&gt;([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt;)
&lt;span class=&quot;keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;func&lt;/span&gt;() {
  &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; {
    &lt;span class=&quot;variable&quot;&gt;n&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;property&quot;&gt;Stdin&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Read&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;inbuf&lt;/span&gt;)
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
      &lt;span class=&quot;function_builtin variable function&quot;&gt;panic&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt;)
    }
    &lt;span class=&quot;variable&quot;&gt;tty&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Write&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;inbuf&lt;/span&gt;[:&lt;span class=&quot;variable&quot;&gt;n&lt;/span&gt;])
    &lt;span class=&quot;variable&quot;&gt;inch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt;
  }
}()

&lt;span class=&quot;variable&quot;&gt;outbuf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;make&lt;/span&gt;([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;4096&lt;/span&gt;)
&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; {
  &lt;span class=&quot;variable&quot;&gt;n&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;tty&lt;/span&gt;.&lt;span class=&quot;property function_method&quot;&gt;Read&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;outbuf&lt;/span&gt;)
  &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
    &lt;span class=&quot;function_builtin variable function&quot;&gt;panic&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt;)
  }
  &lt;span class=&quot;variable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;make&lt;/span&gt;([]&lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;n&lt;/span&gt;)
  &lt;span class=&quot;function_builtin variable function&quot;&gt;copy&lt;/span&gt;(&lt;span class=&quot;variable&quot;&gt;b&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;outbuf&lt;/span&gt;[:&lt;span class=&quot;variable&quot;&gt;n&lt;/span&gt;])
  &lt;span class=&quot;variable&quot;&gt;outch&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;b&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tty will echo characters written to it, so we just write to it from stdin and increase the form feed timeout closer to the user’s input so that it’s not constantly feeding in and out as you write. The resulting system is pretty pleasant to use! I spent about hour working on improvements to it on a &lt;a href=&quot;https://live.drewdevault.com&quot; target=&quot;_blank&quot;&gt;live stream&lt;/a&gt;. You can watch the system in action on the archive here:&lt;/p&gt;&lt;iframe width=&quot;560&quot; height=&quot;370&quot; sandbox=&quot;allow-same-origin allow-scripts&quot;
src=&quot;https://spacepub.space/videos/embed/a8be6c87-9267-452e-8d3e-dd206880fa98&quot;
frameborder=&quot;0&quot; allowfullscreen style=&quot;width: 100%&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;If you were a fly on the wall when Unix was written, it would have looked a lot like this. And remember: &lt;a href=&quot;https://www.gnu.org/fun/jokes/ed-msg.html&quot; target=&quot;_blank&quot;&gt;ed is the standard text editor&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Line-printer-shell-hack/</link>
        
        <pubDate>Wed, 30 Oct 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Line-printer-shell-hack/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, October 2019</title>
          <description>
            &lt;p&gt;Last month, I gave you an update at the conclusion of a long series of travels. But, I wasn’t done yet - this month, I spent a week in Montreal for &lt;a href=&quot;https://xdc2019.x.org/&quot; target=&quot;_blank&quot;&gt;XDC&lt;/a&gt;. Simon Ser put up &lt;a href=&quot;https://emersion.fr/blog/2019/xdc2019-wrap-up/&quot; target=&quot;_blank&quot;&gt;a great write-up&lt;/a&gt; which goes over a lot of the important things we discussed there. It was a wonderful conference and well worth the trip - but I truly am sick of travelling. Now, I can enjoy some time at home, working on free and open source software.&lt;/p&gt;&lt;p&gt;I have a video to share today, of a workflow on git.sr.ht that I’m very excited about: sending patchsets as emails from the web.&lt;/p&gt;&lt;video src=&quot;/l.sr.ht/_fUk.webm&quot; controls muted&gt;
  Your web browser does not support the webm video codec. Please consider using
  web browsers that support free and open standards.
&lt;/video&gt;
&lt;p&gt;Sourcehut’s development plans can be described in three broad strokes: (1) make a bunch of services (or: primitives for a development hub); (2) rig them all up with APIs and webhooks; and (3) teach them how to talk to each other. Over the past year, (1) and (2) are mostly complete, and (3) is now underway. Teaching git.sr.ht and lists.sr.ht to talk to each other is an important step, because it will give us a web-based code review flow which is backed by emails. This meets an original design goal of Sourcehut: to build user-friendly tools on top of existing systems.&lt;/p&gt;&lt;p&gt;The other end of this work is on lists.sr.ht, but for now it’s indirect: I’ve also been working on &lt;a href=&quot;https://github.com/libgit2/pygit2/pulls?q=is%3Apr+author%3Addevault+is%3Aclosed&quot; target=&quot;_blank&quot;&gt;pygit2&lt;/a&gt; fleshing out the Odb backend API, so that I can make a pygit2 repo which is backed by the git.sr.ht API. From there, it’ll be easy to teach lists.sr.ht about git.sr.ht - and perhaps other git services as well.&lt;/p&gt;&lt;p&gt;There’s also a fourth stage of Sourcehut: giving back to the free software community. To this end, I intend to spend Sourcehut’s profits on sponsoring motivated and talented free software developers to work on self-directed projects. I’m very excited to announce that there’s progress here as well: &lt;a href=&quot;https://emersion.fr&quot; target=&quot;_blank&quot;&gt;Simon Ser&lt;/a&gt; is now joining Sourcehut and will be doing just that: self-directed free software projects. He’s written more about this on &lt;a href=&quot;https://emersion.fr/blog/2019/working-full-time-on-open-source/&quot; target=&quot;_blank&quot;&gt;his blog&lt;/a&gt; and I’ll be writing more on &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;sourcehut.org&lt;/a&gt; later.&lt;/p&gt;&lt;p&gt;Wrapping up Sourcehut news, I’ll leave you with an out-of-context screenshot of a mockup I made this month:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/_yhw.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a Sourcehut DNS service showing DNS records managed by zone files in a git repository&lt;/figcaption&gt;&lt;/figure&gt;]&lt;/p&gt;&lt;p&gt;Let’s move on to Wayland news. We’ve started the planning for the next sway release, and it’s shaping up to be really cool. We expect to ship patches which can reduce input latency to as low as 1ms, introduce the foreign toplevel management protocol for better mate-panel support, and introduce damage tracking to our screencopy protocol - which is being used to make a VNC server for sway and other wlroots-based compositors; and proper drawing tablet support. We’re also making strong headway on a long-term project to overhaul rendering and DRM in wlroots, with the long term goal of achieving the holy grail levels of performance on any device.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://wayland-book.com&quot; target=&quot;_blank&quot;&gt;The Wayland book&lt;/a&gt; is also in good shape. A lot of people have purchased the drafts - over a hundred! Thank you for picking it up, and please send your feedback along. I completed chapter 8 this month. I also expect to receive the last few parts for my second POWER9 machine today, and I plan on using this to test Wayland, Mesa, etc - on ppc64le. The &lt;a href=&quot;https://drewdevault.com/2019/10/10/RaptorCS-redemption.html&quot; target=&quot;_blank&quot;&gt;first POWER9 machine&lt;/a&gt; is now provisioned and humming along in the Sourcehut datacenter, by the way.&lt;/p&gt;&lt;p&gt;VR work has also been chugging along again this month. I’ve started contributing to &lt;a href=&quot;https://gitlab.freedesktop.org/monado/monado/merge_requests?scope=all&amp;utf8=%E2%9C%93&amp;state=merged&amp;author_username=ddevault&quot; target=&quot;_blank&quot;&gt;Monado&lt;/a&gt;, which is basically to OpenXR as Mesa is to OpenGL. I’ve seen merged an overhaul to their build system, an overhaul for their dated Wayland backend, and even some deeper work ensuring conformance with the OpenXR specification. A lot of this work has also been in getting to know everyone and planning the future of the project, as it’s still in early stages.&lt;/p&gt;&lt;p&gt;To quickly summarize my other various projects:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;ctools&lt;/strong&gt; has seen many small improvements and bug fixes, and has grown the dirname, rmdir, env, and sleep utilities.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;aerc&lt;/strong&gt; has also seen small improvements and bug fixes, but has also learned about sorting and will soon grow a threaded message list&lt;/li&gt;&lt;li&gt;&lt;strong&gt;chopsui&lt;/strong&gt; is stirring in its sleep, and I’ve been giving some new attention to its design problems in the hopes that the next iteration will be the correct design for a new GUI toolkit.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/wshowkeys&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;wshowkeys&lt;/strong&gt;&lt;/a&gt; is a new little tool I built to display your keypresses on-screen during a Wayland session, useful for live streaming or video recording&lt;/li&gt;&lt;li&gt;&lt;strong&gt;9front&lt;/strong&gt; has been eating some of my evenings lately, and I’ve been making small improvements to various tools and improving Plan 9 support among some packages in the Go ecosystem. I have more plans for this… stay tuned.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That’s all I’ve got for today. Thank you for your support! Oh, and one last note: I’ve been invited to the &lt;a href=&quot;https://github.com/users/ddevault/sponsorship&quot; target=&quot;_blank&quot;&gt;Github sponsors program&lt;/a&gt;, so if you want to donate through it Github will match your donation for a little while. Cheers! /&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-October-2019/</link>
        
        <pubDate>Tue, 15 Oct 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-October-2019/</guid>
      </item>
    
      <item>
        
        
          <title>How to fuck up software releases</title>
          <description>
            &lt;p&gt;I manage releases for a bunch of free &amp; open-source software. Just about every time I ship a release, I find a novel way to fuck it up. Enough of these fuck-ups have accumulated now that I wanted to share some of my mistakes and how I (try to) prevent them from happening twice.&lt;/p&gt;&lt;p&gt;At first, I did everything manually. This is fine enough for stuff with simple release processes - stuff that basically amounts to tagging a commit, pushing it, and calling it a day. But even this gets tedious, and I’d often make a mistake when picking the correct version number. So, I wrote a small script: &lt;a href=&quot;https://paste.sr.ht/~sircmpwn/34bd575b4c37ae6102d6ede2a4ba17b2e1bb9cf8&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt;. &lt;code&gt;semver patch&lt;/code&gt; bumps the patch version, &lt;code&gt;semver minor&lt;/code&gt; bumps the minor version, and &lt;code&gt;semver major&lt;/code&gt; bumps the major version, based on semantic versioning. I got into the habit of using this script instead of making the tags manually. The next fuckup soon presented itself: when preparing the &lt;a href=&quot;https://git-scm.com/docs/git-shortlog&quot; target=&quot;_blank&quot;&gt;shortlog&lt;/a&gt;, I would often feed it the wrong commits, and the changelog would be messed up. So, I updated the script to run the appropriate shortlog command and pre-populate the annotated tag with it, launching the editor to adjust the changelog as necessary.&lt;/p&gt;&lt;p&gt;Soon I wanted to apply this script to other projects, but not all of them used semantic versioning. I updated it to work for projects which just use &lt;code&gt;major.minor&lt;/code&gt; versions as well. However, another problem arose: some projects have the version number specified in the Makefile or meson.build. I would frequently fuck this up in many creative ways: forgetting it entirely; updating it but not committing it; updating it and committing it, but tagging the wrong commit; etc. &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt; in particular was difficult because I also had to update the soversion, which had special requirements. To address these issues, I added a custom &lt;code&gt;.git/_incr_version&lt;/code&gt; script which can add additional logic on a per-repo basis, and updated semver to call this script if present.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Eventually, I went on vacation and shipped a release while I was there. The &lt;code&gt;_incr_version&lt;/code&gt; script I had put into &lt;code&gt;.git&lt;/code&gt; on my home workstation wasn’t checked into version control and didn’t come with me on vacation, leading to yet another fucked up release. I moved it from &lt;code&gt;.git/_incr_version&lt;/code&gt; to &lt;code&gt;contrib/_incr_version&lt;/code&gt;. I made the mistake, however, of leaving the old path in as a fallback, which meant that I never noticed that &lt;em&gt;another&lt;/em&gt; project’s script was still in &lt;code&gt;.git&lt;/code&gt; until I went on another vacation and fucked up another release. Add a warning which detects if the script is at the old path…&lt;/p&gt;&lt;p&gt;Some of my projects don’t use semantic versioning at all, but still have all of these other gotchas, so I added an option to just override the automatic version increment with a user-specified override. For a while, this worked well. But, inevitably, no matter how much I scripted away my mistakes I would always find a new and novel way of screwing up. The next one came when I shipped a release while on an Alpine Linux machine, which ships Busybox instead of GNU tools. Turns out Busybox gzip produces output which does not match the GNU output, which means the tarballs I signed locally differed from the ones generated by Github. Update the signing script to save the tarball to disk (previously, it lived in a pipe) and upload these alongside the releases…&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Surely, there are no additional ways to fuck it up at this point. I must have every base covered, right? Wrong. Dead wrong. On the very next release I shipped, I mistakenly did everything from a feature branch, and shipped experimental, incomplete code in a stable release. Update the script to warn if the master branch isn’t checked out… Then, of course, another fuckup: I tagged a release without pulling first, and when I pushed, git happily rejected my branch and accepted the tag - shipping an outdated commit as the release. Update the script to &lt;code&gt;git pull&lt;/code&gt; first…&lt;/p&gt;&lt;p&gt;I am doomed to creatively outsmart my tools in releases. If you’d like to save yourself from some of the mistakes I’ve made, you can &lt;a href=&quot;https://paste.sr.ht/~sircmpwn/34bd575b4c37ae6102d6ede2a4ba17b2e1bb9cf8&quot; target=&quot;_blank&quot;&gt;find my semver script here&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/how-to-fuck-up-releases/</link>
        
        <pubDate>Sat, 12 Oct 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/how-to-fuck-up-releases/</guid>
      </item>
    
      <item>
        
        
          <title>RaptorCS&apos;s redemption: the POWER9 machine works</title>
          <description>
            &lt;p&gt;This is a follow-up to my earlier article, “&lt;a href=&quot;https://drewdevault.com/2019/09/23/RaptorCS-Blackbird-a-horror-story.html&quot; target=&quot;_blank&quot;&gt;RaptorCS POWER9 Blackbird PC: An expensive mistake&lt;/a&gt;”. Since I published that article, I’ve been in touch with Raptor and they’ve been much more communicative and helpful. I now have a working machine!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Update Feb. 2024: Seems the improvements I asked for may not have stuck. &lt;a href=&quot;https://posixcafe.org/blogs/2024/02/26/0/&quot; target=&quot;_blank&quot;&gt;Buyer beware&lt;/a&gt;.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://sr.ht/OTyo.jpeg&quot;&gt;
&lt;figcaption&gt;Picture of uname -sm showing “Linux ppcle64”&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;After I published my article, Raptor reached out and apologised for my experience. They offered a full refund, but I agreed to work on further diagnosis now that we had opened a dialogue&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. They identified that my CPU was defective and sent me a replacement, then we found the mainboard to be defective, too, and the whole thing was shipped back and replaced. I installed the new hardware into the datacenter today and it was quite pleasant to get up and running. Raptor assures me that my nightmares with the old board are atypical, and if the new board is representative of the usual user experience, I would have to agree. The installation was completely painless.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;However, I refuse to give any company credit for waking up their support team only when a scathing article about them frontpages on Hacker News. I told them I wouldn’t publish a positive follow-up unless they also convinced me that the support experience had been fixed for the typical user as well. To this end, Raptor has made a number of substantive changes. To quote their support staff:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;After investigation, we are implementing new mechanisms to avoid support issues like the one you experienced. We now have a &lt;a href=&quot;https://twitter.com/RaptorCompSys/status/1176432946670186498&quot; target=&quot;_blank&quot;&gt;self-serve RMA generation system&lt;/a&gt; which would have significantly reduced your wait time, and are taking measures to ensure that tickets are no longer able to be ignored by front line support staff. We believe we have addressed the known failure modes at this time, and management will be keeping a close eye on the operation of the support system to ensure that new failure modes are handled rapidly.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;They’ve tweeted this about their new self-service RMA system as well:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We’ve made it easy to submit RMA requests for defective products on our Web site. Simply go to your account, select the “Submit RMA Request” link, and fill out the form.  Your product will be warranty checked and, if valid, you will receive an RMA number and shipping address!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;— @RaptorCompSys via &lt;a href=&quot;https://twitter.com/RaptorCompSys/status/1176432946670186498&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;p&gt;They’re also working on other improvements to make the end-user experience better, including &lt;a href=&quot;https://wiki.raptorcs.com/wiki/Main_Page&quot; target=&quot;_blank&quot;&gt;more content on the wiki&lt;/a&gt;, such as a &lt;a href=&quot;https://wiki.raptorcs.com/wiki/Troubleshooting/Support_Request_Checklist&quot; target=&quot;_blank&quot;&gt;flowchart for dealing with common problems&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Thanks to Raptor for taking the problem seriously, quickly fixing the problems with my board, and for addressing the systemic problems which led to the failure of their support system.&lt;/p&gt;&lt;p&gt;On the subject of the working machine, I am quite impressed with it so far. Installation was a breeze, it compiles the kernel on 32 threads from spinning rust in 4m15s, and I was able to get KVM working without much effort. I have christened it “flandre”&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, which I think is fitting. I plan on bringing it up as a build slave for builds.sr.ht in the coming weeks/months, and offering ppc64le builds on Sourcehut in the near future. I have another board which was generously donated by another Raptor customer&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, which arrived last week and that I hope to bring up and use for testing Wayland before introducing it to the Sourcehut fleet.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;P.S. For those interested in more details of the actual failures:&lt;/p&gt;&lt;p&gt;This machine is so badly broken that it would actually be hilarious if the manufacturer had been more present in the troubleshooting process. I think the best way to sum it up is “FUBAR”. Among problems I encountered were:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The CPU experiences a “ZCAL failure” (???)&lt;/li&gt;&lt;li&gt;The BMC (responsible for bringing up the main CPU(s)) had broken ethernet, making login over SSH impossible&lt;/li&gt;&lt;li&gt;The BMC’s getty would boot loop, making login over serial impossible&lt;/li&gt;&lt;li&gt;The BMC’s u-Boot would boot loop if the TX pin on the serial cable was plugged in, making diagnosing issues from that stage impossible&lt;/li&gt;&lt;li&gt;petitboot’s ncurses output was being piped into a shell and executed (what the fuck?)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the immortal words of James Mickens, “I HAVE NO TOOLS BECAUSE I HAVE DESTROYED MY TOOLS WITH MY TOOLS.” A staff member at Raptor tells me: “Your box ended up on my desk […] This is easily the most broken board I’ve seen, ever, and that includes prototypes. This will help educate us for a while to come due to the unique nature of some of the faults.”&lt;/p&gt;&lt;p&gt;Not sure what can cause such an impressive cacophony of failures, but it’s so catastrophic that I can easily believe that this is far from typical. The hardware is back in Raptor’s hands now, and I would be interested to hear about their insights after further diagnosis.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/RaptorCS-redemption/</link>
        
        <pubDate>Thu, 10 Oct 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/RaptorCS-redemption/</guid>
      </item>
    
      <item>
        
        
          <title>Why Collabora really added Digital Restrictions Management to Weston</title>
          <description>
            &lt;p&gt;A recent article from Collabora, &lt;a href=&quot;https://www.collabora.com/news-and-blog/blog/2019/10/03/why-hdcp-support-in-weston-is-a-good-thing/&quot; target=&quot;_blank&quot;&gt;Why HDCP support in Weston is a good thing&lt;/a&gt;, proports to offer a lot of insight into why &lt;a href=&quot;https://en.wikipedia.org/wiki/High-bandwidth_Digital_Content_Protection&quot; target=&quot;_blank&quot;&gt;HDCP&lt;/a&gt; - a Digital Restrictions Management (DRM) related technology - was added to &lt;a href=&quot;https://gitlab.freedesktop.org/wayland/weston&quot; target=&quot;_blank&quot;&gt;Weston&lt;/a&gt; - a well known basic Wayland compositor which was once the reference compositor for Wayland. But this article is gaslighting you. There is one reason and one reason alone that explains why HDCP support landed in Weston.&lt;/p&gt;&lt;p&gt;Q: Why was HDCP added to Weston?&lt;/p&gt;&lt;p&gt;A: $$$$$&lt;/p&gt;&lt;p&gt;Why does Collabora want you to &lt;em&gt;believe&lt;/em&gt; that HDCP support in Weston is a good thing? Let’s look into this in more detail. First: &lt;em&gt;is&lt;/em&gt; HDCP a bad thing?&lt;/p&gt;&lt;p&gt;DRM (Digital Restrictions Management) is the collective term for software which attempts to restrict the rights of users attempting to access digital media. It’s mostly unrelated to Direct Rendering Manager, an important Linux subsystem for graphics which is closely related to Wayland. Digital Restrictions Management is software used by media owners to prevent you from enjoying their content except in specific, pre-prescribed ways.&lt;/p&gt;&lt;p&gt;There is universal agreement among the software community that DRM is ineffective. Ultimately, these systems are defeated by the simple fact that no amount of DRM can stop you from pointing your camera at your screen and pushing record. But in practice, we don’t even need to resort to that - these systems are far too weak to demand such measures. &lt;a href=&quot;https://www.amazon.com/HSV321/dp/B07C6KCBYB&quot; target=&quot;_blank&quot;&gt;Here’s a $100 device on Amazon which can break HDCP&lt;/a&gt;. DRM is shown to be impossible even in &lt;em&gt;theory&lt;/em&gt;, as the decryption keys have to live somewhere in your house in order to watch movies there. Exfiltrating them is just a matter of putting forth the effort.  For most users, it hardly requires any effort to bypass DRM - they can just punch “watch [name of movie] for free” into Google. It’s well-understood and rather obvious that DRM systems completely and entirely fail at their stated goal.&lt;/p&gt;&lt;p&gt;No reasonable engineer would knowingly agree to adding a broken system like that to their system, and trust me - the entire engineering community has been made well-aware of these faults. Any other system with these obvious flaws would be discarded immediately, and if the media industry hadn’t had their hands firmly clapped over their ears, screaming “la la la”, and throwing money at the problem, it would have been. But, just adding a broken system isn’t necessarily going to hurt much.  The problem is that, in its failure to achieve its stated goals, DRM brings with it some serious side-effects. DRM is closely tied to nonfree software - the RIAA mafia wants to keep their garbage a secret, after all. Moreover, DRM takes away the freedom to play your media when and where you want. Why should you have to have an internet connection? Why can’t you watch it on your ancient iPod running Rockbox? DRM exists to restrict users from doing what they want. More sinisterly, it exists to further the industry’s push to end consumer ownership of its products - preferring to steal from you monthly subscription fees and lease the media to you. Free software maintainers are responsible for protecting their users from this kind of abuse, and putting DRM into our software betrays them.&lt;/p&gt;&lt;p&gt;The authors are of the opinion that HDCP support in Weston does not take away any rights from users. It doesn’t &lt;em&gt;stop&lt;/em&gt; you from doing anything. This is true, in the same way that killing environmental regulations doesn’t harm the environment. Adding HDCP support is handing a bottle of whiskey to an abusive husband. And the resulting system - and DRM as a whole - is known to be inherently broken and ineffective, a fact that they even acknowledge in their article. This feature &lt;em&gt;enables&lt;/em&gt; media companies to abuse &lt;em&gt;your&lt;/em&gt; users. Enough cash might help some devs to doublethink their way out of it, but it’s true all the same. They added these features to help abusive companies abuse their users, in the hopes that they’ll send back more money or more patches. They say as much in the article, it’s no secret.&lt;/p&gt;&lt;p&gt;Or, let’s give them the benefit of the doubt: perhaps their bosses forced them to add this&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. There have been other developers on this ledge, and I’ve talked them down. Here’s the thing: it worked. Their organizations didn’t pursue DRM any further. You are not the lowly code monkey you may think you are. Engineers have real power in the organization. You can say “no” and it’s your responsibility to say “no” when someone asks you to write unethical code.&lt;/p&gt;&lt;p&gt;Some of the people I’ve spoken to about HDCP for Wayland, particularly for Weston, are of the opinion that “a protocol for it exists, therefore we will implement it”. This is reckless and stupid. We already know what happens when you bend the knee to our DRM overlords: look at Firefox. In 2014, Mozilla added DRM to Firefox after a year of fighting against its standardization in the W3C (a &lt;a href=&quot;https://en.wikipedia.org/wiki/Regulatory_capture&quot; target=&quot;_blank&quot;&gt;captured&lt;/a&gt; organization which governs&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; web standards). They capitulated, and it did absolutely nothing to stop them from being steamrolled by Chrome’s growing popularity. Their market-share freefall didn’t even slow down in 2014, or in any year since&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. Collabora went down without a fight in the first place.&lt;/p&gt;&lt;p&gt;Anyone who doesn’t recognize that self-interested organizations with a great deal of resources are working against &lt;em&gt;our&lt;/em&gt; interests as a free software community is an idiot. We are at war with the bad actors pushing these systems, and they are to be &lt;a href=&quot;https://en.wikipedia.org/wiki/No_quarter&quot; target=&quot;_blank&quot;&gt;given no quarter&lt;/a&gt;. Anyone who realizes this and turns a blind eye to it is a coward. Anyone who doesn’t stand up to their boss, sits down, implements it in our free software ecosystem, and cashes their check the next Friday - is not only a coward, but a traitor to their users, their peers, and to society as a whole.&lt;/p&gt;&lt;p&gt;“HDCP support in Weston is a good thing”? It’s a good thing for &lt;em&gt;you&lt;/em&gt;, maybe. It’s a good thing for media conglomerates which want our ecosystem crushed underfoot. It’s a bad thing for your users, and you know it, Collabora. Shame on you for gaslighting us.&lt;/p&gt;&lt;p&gt;However… the person who &lt;em&gt;reverts&lt;/em&gt; these changes is a hero, even in the face of past mistakes. Weston, Collabora, you still have a chance to repent. Do what you know is right and stand by those principles in the future.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;P.S. To make sure I’m not writing downers all the time, rest assured that the next article will bring good news - RaptorCS has been working hard to correct the issues I raised in my last article.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/HDCP-in-Weston/</link>
        
        <pubDate>Mon, 07 Oct 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/HDCP-in-Weston/</guid>
      </item>
    
      <item>
        
        
          <title>RaptorCS POWER9 Blackbird PC review</title>
          <description>
            &lt;p&gt;&lt;strong&gt;November 2018&lt;/strong&gt;: Ordered &lt;a href=&quot;https://www.raptorcs.com/content/BK1B01/intro.html&quot; target=&quot;_blank&quot;&gt;Basic Blackbird Bundle&lt;/a&gt; w/32 GB RAM: $1,935.64&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update 2019-12-23&lt;/strong&gt;: This article was originally titled “RaptorCS POWER9 Blackbird PC: An expensive mistake”. Please read the follow-up article, published 2019-10-10: &lt;a href=&quot;https://drewdevault.com/2019/10/10/RaptorCS-redemption.html&quot; target=&quot;_blank&quot;&gt;RaptorCS’s redemption: the POWER9 machine works&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;June 2019&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Order ships, and arrives without RAM. It had been long enough that I didn’t realize the order had only been partially fulfilled, so I order some RAM from the &lt;a href=&quot;https://wiki.raptorcs.com/wiki/POWER9_Hardware_Compatibility_List/Memory&quot; target=&quot;_blank&quot;&gt;list of recommended chips&lt;/a&gt; ($338.40), along with the other necessities that I didn’t purchase from Raptor: a case ($97.99) and a PSU ($68.49), and grab some hard drives I have lying around. Total cost: about $2,440. Worth it to get POWER9 builds working on builds.sr.ht!&lt;/p&gt;&lt;p&gt;I carefully put everything together, consulting the manual at each step, plug in a display, and turn it on. Lights come on, things start whizzing, and the screen comes to life - and promptly starts boot looping.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;June 27th&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Support ticket created. What’s going on with my board?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;June 28th&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Support gets back to me the next day with a suggestion which is unrelated to the problem, but no matter - I spoke with volunteers in the IRC channel a few hours earlier and we found out that - whoops! - I hadn’t connected the CPU power to the motherboard. This is the end of the PEBKAC errors, but not the end of the problems. The machine gets further ahead in the boot - almost to “petitboot”, and then the display dies and the machine reveals no further secrets.&lt;/p&gt;&lt;p&gt;I sent an update to the support team.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;July 1st&lt;/strong&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We have normally only seen this type of failure when there is a RAM-related fault, or if the PSU is underpowered enough that bringing the CPUs online at full power causes a power fault and immediate safety power off.&lt;/p&gt;&lt;p&gt;Can you watch the internal lights while the system is booting, and see if the power LED cluster immediately changes from green to orange as the system stops responding over SSH?&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The IRC channel suspects this is not related to the problem. Regardless, I reply a few hours later with two videos showing the boot up process from power-out to display death, with the internal LEDs and the display output clearly visible.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;July 4th&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;“Any progress on this issue?”, I ask.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;July 15th&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;“Hi guys, I’m still experiencing this problem. If you’re unsure of the issue I would like to send the board back to you for diagnosis or a refund.”&lt;/p&gt;&lt;p&gt;&lt;strong&gt;July 25th&lt;/strong&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Sorry for the delay. Having senior support check out the videos.&lt;/p&gt;&lt;p&gt;Thanks for writing back. We should have something for you by tomorrow during the day.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;July 31st&lt;/strong&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Hi Drew.&lt;/p&gt;&lt;p&gt;The videos are being reviewed this week. Thank you for sending them.&lt;/p&gt;&lt;p&gt;Please stay tuned.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;September 15th&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;No reply from support. I have since bought a little more hardware for self-diagnosis, namely the necessary pieces to connect to the two (or is it 3?) serial ports. I manage to get a log, which points to several failures, but none of them seem to be related to the problem at hand (they do indicate some network failures, which would explain why I can’t log into the BMC over SSH for further diagnosis). And the getty is looping, so I can’t log in on the serial console to explore any further.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;That was a week ago. Radio silence since.&lt;/p&gt;&lt;p&gt;So, 10 months after I placed an order for a POWER9 machine, 3 months after I received it (without the RAM I purchased, no less), and over $2,500 invested… it’s clear that buying the Blackbird was an expensive mistake. Maybe someday I’ll get it working. If I do, I doubt the “support” team will have been involved. Currently my best bet seems to be waiting for some apparent staff member (the only apparent staff member) who idles in the IRC channel on Freenode and allegedly comes online from time to time.&lt;/p&gt;&lt;p&gt;I’m not alone in these problems. Here are some (anonymized) quotes I’ve heard from others while trying to troubleshoot this on IRC.&lt;/p&gt;&lt;p&gt;On support:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;ugh, ddevault, yeah. [Blackbird ownership] has not been a smooth experience for me, either.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;my personal theory is that they have really bad ticket software that ‘loses’ tickets somehow&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;On reliability:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I’ve found openbmc’s networking to be… a bit unreliable… maybe 20% of the time it does not responed[sic]/does not respond fast enough to networking requests.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;yeah the vga handoff failing doesn’t surprise me (other people here have reported it). but the BMC not getting a DHCP lease is odd. (well maybe not that odd if you look at the crumminess of the OpenBMC software stack…)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;So, yeah, don’t buy from Raptor Computer Systems. It’s too large and unwieldly to be an effective paper weight, either!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Erratta&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;2019-09-24 @ 00:19 UTC&lt;/em&gt;: Raptor has reached out and apologized for my support experience. We are discussing these problems in more detail now. They have also issued a refund for the unshipped RAM.&lt;/p&gt;&lt;p&gt;&lt;em&gt;2019-09-24 @ 00:51 UTC&lt;/em&gt;: Raptor believes the CPU to be faulty and is shipping a replacement. They attribute the delay to having to reach out to IBM about the problem, but don’t have a satisfactory answer to why the support process failed. I understand it’s being discussed internally.&lt;/p&gt;&lt;p&gt;&lt;em&gt;2019-09-24 @ 13:08 UTC&lt;/em&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;After investigation, we are implementing new mechanisms to avoid support issues like the one you experienced. We now have a self-serve RMA generation system which would have significantly reduced your wait time, and are taking measures to ensure that tickets are no longer able to be ignored by front line support staff. We believe we have addressed the known failure modes at this time, and management will be keeping a close eye on the operation of the support system to ensure that new failure modes are handled rapidly.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;They’ve tweeted this about their new self-service RMA system as well:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We’ve made it easy to submit RMA requests for defective products on our Web site. Simply go to your account, select the “Submit RMA Request” link, and fill out the form.  Your product will be warranty checked and, if valid, you will receive an RMA number and shipping address!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;— @RaptorCompSys via &lt;a href=&quot;https://twitter.com/RaptorCompSys/status/1176432946670186498&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I agree that this shows positive improvements and a willingness to continue making improvements in their support experience. Thanks to Raptor for taking these concerns seriously. I hope to have a working Blackbird system soon, and will publish a follow-up review when the time comes.&lt;/p&gt;&lt;p&gt;&lt;em&gt;2019-10-08 @ 22:30 UTC&lt;/em&gt; A source quoted anonymously in this article asked me to remove their quote, after a change of heart. They feel that the attention this article has received has made their statement reach beyond the level of dissatisfaction they had with Raptor at the time.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/RaptorCS-Blackbird-a-horror-story/</link>
        
        <pubDate>Mon, 23 Sep 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/RaptorCS-Blackbird-a-horror-story/</guid>
      </item>
    
      <item>
        
        
          <title>Don&apos;t sacrifice the right ideas to win the right words</title>
          <description>
            &lt;p&gt;There is a difference between free software and open-source software. But you have to squint to see it. Software licenses which qualify for one title but not the other are exceptionally rare.&lt;/p&gt;&lt;p&gt;A fascination with linguistics is common among hackers, and I encourage and participate in language hacking myself. Unfortunately, that seems to seep into the Free Software Foundation’s message a bit too much. Let’s see if any of this rings familiar:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;It’s not actually open source, but free software. You see, “open source” is a plot by the commercial software industry to subvert the “free software” movement…&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;No, it’s free-as-in-freedom, not free-as-in-beer. Sometimes we call it “libre” software, borrowing the French or Spanish word, because in English…&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;What you’re referring to as Linux, is in fact, GNU/Linux, or as I’ve recently taken to calling it, GNU plus Linux. Linux is not an operating system…&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;What do all of these have in common? The audience already agrees with the speaker on the ideas, but this becomes less so with every word. This kind of pedantry lacks tact and pushes people away from the movement. No one wants to talk to someone who corrects them like this, so people shut down and stop listening. The speaker gains the self-satisfaction that comes with demonstrating that you’re smarter than someone else, but the cost is pushing that person away from the very ideals you’re trying to clarify. This approach doesn’t help the movement, it’s just being a dick.&lt;/p&gt;&lt;p&gt;For this reason, even though I fully understand the difference between free and open-source software, I use the terms basically interchangeably. In practice they are effectively the same thing. Then, I preach the ideologies behind free software even when discussing open-source software.  The ideas are what matters, the goal is to get people thinking on your wavelength. If they hang around long enough, they’ll start using your words, too.  That’s how language works.&lt;/p&gt;&lt;p&gt;The crucial distinction of the free software movement is less about “free software”, after all, and more about copyleft. But, because the FSF pushes copyleft &lt;em&gt;and&lt;/em&gt; free software, and because many FSF advocates are pedantic and abrasive, many people check out before they’re told the distinction between free software and copyleft. This leads to the listener &lt;em&gt;equivocating&lt;/em&gt; free software with copyleft software, which undermines the message and hurts both.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;This lack of tact is why I find it difficult to accept the FSF as a representative of the movement I devote myself to. If your goal is to strengthen the resolve and unity of people who already agree with you by appealing to tribalism, then this approach is effective - but remember that it strengthens the opposing tribes, too. If your goal is to grow the movement and win the hearts and minds of the people, then you need to use more tact in your language. Turn that hacker knack for linguistic hacking towards &lt;em&gt;this&lt;/em&gt; goal, of thinking over how your phrasing and language makes different listeners feel. The resulting literature will be much more effective.&lt;/p&gt;&lt;p&gt;Attack the systems and individuals who brought about the circumstances that frustrate your movement, but don’t attack their victims. It’s not the user’s fault that they were raised on proprietary software. The system which installed proprietary software on their school computers is the one to blame. Our goals should be things like introducing Linux to the classroom, petitioning our governments to require taxpayer-funded software to be open source, eliminating Digital Restrictions Management&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, pushing for right to repair, and so on. Why is “get everyone to say ‘libre’ instead of ‘open-source’” one of our goals instead?&lt;/p&gt;&lt;p&gt;An aside: sometimes language &lt;em&gt;is&lt;/em&gt; important. When someone has the wrong words but the right ideas, it’s not a big deal. When someone has the wrong &lt;em&gt;ideas&lt;/em&gt; and is appropriating the words to support them, that’s a problem. This is why I still come down hard on companies which gaslight users with faux-open software licenses like the Commons Clause or the debacle with RedisLabs.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: this article is not about Richard Stallman. I have no comment on the recent controversies.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-wrong-words-but-the-right-ideas/</link>
        
        <pubDate>Tue, 17 Sep 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-wrong-words-but-the-right-ideas/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, September 2019</title>
          <description>
            &lt;p&gt;Finally home again after a long series of travels! I spent almost a month in Japan, then visited my sister’s new home in Hawaii on the way eastwards, then some old friends in Seattle, and finally after 5½ long weeks, it’s home sweet home here in Philadelphia. At least until I leave for &lt;a href=&quot;https://xdc2019.x.org/&quot; target=&quot;_blank&quot;&gt;XDC&lt;/a&gt; in Montreal 2 weeks from now. Someday I’ll have some rest… throughout all of these wild travels, I’ve been hard at work on my free software projects. Let’s get started with this month’s status update!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/iuDE.jpg&quot;&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;small&gt;Great view from a hike on O&apos;ahu&lt;/small&gt;
&lt;/p&gt;
&lt;p&gt;First, Wayland news. I’m happy to share with you that the Wayland book is now more than halfway complete, and I’ve made the drafts available online for a discounted price: &lt;a href=&quot;https://wayland-book.com&quot; target=&quot;_blank&quot;&gt;The Wayland Protocol&lt;/a&gt;. Thanks to all of my collaborators and readers who volunteered to provide feedback! There’s more Wayland-related news still, as this month marked the release of &lt;a href=&quot;https://github.com/swaywm/sway/releases/tag/1.2&quot; target=&quot;_blank&quot;&gt;sway 1.2&lt;/a&gt; and &lt;a href=&quot;https://github.com/swaywm/wlroots/releases/tag/0.7.0&quot; target=&quot;_blank&quot;&gt;wlroots 0.7.0&lt;/a&gt;. I like this release because it’s light on new features - showing that sway is maturing into a stable and reliable Wayland desktop. The features which were added are subtle and serve to improve sway’s status as a member of the broader ecosystem - sway 1.2 supports the new &lt;a href=&quot;https://github.com/mate-desktop/mate-panel/pull/991&quot; target=&quot;_blank&quot;&gt;layer shell support in the MATE panel&lt;/a&gt;, and the same improvements are already helping with the development of other software.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/9Oro.png&quot;&gt;
&lt;figcaption&gt;Screenshot of MATE panel running on sway&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;small&gt;Rest assured, the weird alignment issues were fixed&lt;/small&gt;
&lt;/p&gt;
&lt;p&gt;On the topic of &lt;a href=&quot;https://aerc-mail.org&quot; target=&quot;_blank&quot;&gt;aerc&lt;/a&gt;, I still haven’t gotten around to that write-up responding to &lt;a href=&quot;http://www.kroah.com/log/blog/2019/08/14/patch-workflow-with-mutt-2019/&quot; target=&quot;_blank&quot;&gt;Greg KH’s post&lt;/a&gt;… but I will. Travels have made it difficult to sit down for a while and do some serious long-term project planning. Regardless, the current plans have still been being executed well. Notmuch support continues to improve thanks to Reto Brunner’s help, completions are improving throughout, and heaps of little features - signatures, unread message counts, :prompt, forward-as-attachment - are now supported.&lt;/p&gt;&lt;p&gt;I also spent some time this month working on Simon Ser’s &lt;a href=&quot;https://mrsh.sh&quot; target=&quot;_blank&quot;&gt;mrsh&lt;/a&gt;. I cleaned up call frames, implemented the &lt;code&gt;return&lt;/code&gt; builtin, finished the &lt;code&gt;pwd&lt;/code&gt; builtin, improved readline support, fleshed out job control, and made many other small improvements. With mrsh nearing completion, I’ve started up another project: &lt;a href=&quot;https://git.sr.ht/~sircmpwn/ctools&quot; target=&quot;_blank&quot;&gt;ctools&lt;/a&gt;. This provides the rest of the POSIX commands required of a standard scripting environment (it replaces coreutils or busybox). I’m taking this one pretty seriously from the start - every command has full POSIX.1-2017 support with a conformance test and a man page, in one C source file and no dependencies. If you’re looking for a good afternoon project (or weekend, for some utilities), how about picking up your favorite &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/&quot; target=&quot;_blank&quot;&gt;POSIX&lt;/a&gt; tool and sending along an implementation?&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://builds.sr.ht/~sircmpwn/job/88955&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/DSxS.png&quot;&gt;
&lt;figcaption&gt;Screenshot of ctools test suite&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;With these projects, along with ~mcf’s &lt;a href=&quot;https://git.sr.ht/~mcf/cproc&quot; target=&quot;_blank&quot;&gt;cproc&lt;/a&gt;, we’re starting to see a simple and elegant operating system come together - exactly the kind I wish we already had. To track our progress towards this goal, I’ve put up &lt;a href=&quot;https://arewesimpleyet.org&quot; target=&quot;_blank&quot;&gt;arewesimpleyet.org&lt;/a&gt;. A day may soon come when computers become the again elegant and simple tools they were always meant to be! At least if we assume “within a few decades” as a valid definition of “soon”.&lt;/p&gt;&lt;p&gt;To cover SourceHut news briefly: we hit 10,000 users this month! And it’s continued to grow since, up to 10,649 users at the time of writing. On the subject of feature development, with Denis Laxalde’s help we’re starting to put together a Debian repository for installing the services on Debian hosts. On todo.sr.ht, users without accounts can now create and comment on tickets via email. I also redesigned &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;sourcehut.org&lt;/a&gt;, adding a blog with a greater breadth of topics than we’ll see on the sr.ht-announce mailing list.&lt;/p&gt;&lt;p&gt;That’s all for this month! I enjoyed my vacation and some much needed time away from work… though for me a “day off” is a day where I write less than 1,000 lines of code. Thank you again for your support - it means the world to me. I’ll see you next month!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/1cuE.jpg&quot;&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;small&gt;Had the best seats at a concert in Tokyo!&lt;/small&gt;
&lt;/p&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-September-2019/</link>
        
        <pubDate>Sun, 15 Sep 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-September-2019/</guid>
      </item>
    
      <item>
        
        
          <title>How I decide between many programming languages</title>
          <description>
            &lt;p&gt;I have a few old standards in my toolbelt that I find myself calling upon most often, but I try to learn enough about many programming languages to reason about whether or not they’re suitable to any use-case I’m thinking about. The best way is to learn by doing, so getting a general impression of the utility of many languages helps equip you with the knowledge of whether or not they’d be useful for a particular problem even if you don’t know them yet.&lt;/p&gt;&lt;p&gt;Only included are languages which I feel knowledgable enough about to comment on, there are many that aren’t here and which I encourage you to research.&lt;/p&gt;&lt;h2&gt;C&lt;/h2&gt;&lt;p&gt;Pros: good performance, access to low-level tooling, useful in systems programming, statically typed, standardized and venerable, the lingua franca, universal support on all platforms.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Cons: string munging, extensible programming, poor availability of ergonomic libraries in certain domains, has footguns, some programmers in the wild think the footguns are useful.&lt;/p&gt;&lt;style&gt;
.bullshit {
    color: white;
    font-style: italic;
    font-weight: bold;
    text-shadow: -1px -1px 0 #000,
           1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}
&lt;/style&gt;
&lt;h2&gt;Go&lt;/h2&gt;&lt;p&gt;Pros: fast, conservative, good package manager and a healthy ecosystem, standard library is well designed, best in class for many problems, has a spec and multiple useful implementations, easy interop with C.&lt;/p&gt;&lt;p&gt;Cons: the runtime is too complicated, no distinction between green threads and real threads (meaning all programs deal with the problems of the latter).&lt;/p&gt;&lt;h2&gt;Rust&lt;/h2&gt;&lt;p&gt;Pros: it’s &lt;span class=&quot;bullshit &quot;&gt;SAFE&lt;/span&gt;, useful for systems programming, better than C++, ecosystem which is diverse but just short of the npm disease, easy interop with C.&lt;/p&gt;&lt;p&gt;Cons: far too big, non-standardized, only one meaningful implementation.&lt;/p&gt;&lt;h2&gt;Python&lt;/h2&gt;&lt;p&gt;Pros: easy and fast to get things done, diverse package ecosystem of reasonably well designed packages, deeply extensible, useful for server-side web software.&lt;/p&gt;&lt;p&gt;Cons: bloated, poor performance, dynamically typed, cpython internals being available to programmers has led to an implementation monoculture.&lt;/p&gt;&lt;h2&gt;JavaScript&lt;/h2&gt;&lt;p&gt;&lt;em&gt;* and all of its derivatives, which ultimately inherit its problems.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Pros: functional but with an expressive and C-like syntax, ES6 improved on many fronts, async/await/promises are well designed, no threading.&lt;/p&gt;&lt;p&gt;Cons: dynamic types, package ecosystem is a flaming pile, many JS programmers aren’t very good at it and they make ecosystem-defining libraries anyway, born in web browsers and inherited their many flaws.&lt;/p&gt;&lt;h2&gt;Java&lt;/h2&gt;&lt;p&gt;&lt;em&gt;* and all of its derivatives, which ultimately inherit its problems.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Pros: has had enough long-term investment to be well understood and reasonably fast.&lt;/p&gt;&lt;p&gt;Cons: hella boilerplate, missing lots of useful things, package management, XML is everywhere, not useful for low-level programming (this applies to all Java-family languages).&lt;/p&gt;&lt;h2&gt;C#&lt;/h2&gt;&lt;p&gt;Pros: less boilerplate than Java, reasonably healthy package ecosystem, good access to low level tools for interop with C, async/await started here.&lt;/p&gt;&lt;p&gt;Cons: ecosystem is in turmoil because Microsoft cannot hold a singular vision, they became open-source too late and screwed over Mono.&lt;/p&gt;&lt;h2&gt;Haskell&lt;/h2&gt;&lt;p&gt;&lt;em&gt;* and every other functional-oriented programming language in its class, such as elixir, erlang, most lisps, even if they resent being lumped together&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Pros: it’s &lt;span class=&quot;bullshit &quot;&gt;FUNCTIONAL&lt;/span&gt;, reasonably fast, useful when the answer to your problem is more important than the means by which you find it, good for research-grade&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; compilers.&lt;/p&gt;&lt;p&gt;Cons: it’s &lt;span class=&quot;bullshit &quot;&gt;FUNCTIONAL&lt;/span&gt;, somewhat inscrutable, awful package management, does not fit well into its environment, written by people who wish the world could be described with a pure function and design software as if it could.&lt;/p&gt;&lt;h2&gt;Perl&lt;/h2&gt;&lt;p&gt;Pros: &lt;a href=&quot;https://github.com/Perl/perl5/blob/blead/Configure&quot; target=&quot;_blank&quot;&gt;entertaining&lt;/a&gt;, best in class at regexes/string munging, useful for making hacky kludges when such solutions are appropriate.&lt;/p&gt;&lt;p&gt;Cons: inscrutable, too extensible, too much junk/jank.&lt;/p&gt;&lt;h2&gt;Lua&lt;/h2&gt;&lt;p&gt;Pros: embeddable &amp; easily plugged into its host, fairly simple, portable.&lt;/p&gt;&lt;p&gt;Cons: 1-based indexing is objectively bad, the upstream maintainers are kind of doing their own thing and no one really likes it.&lt;/p&gt;&lt;h2&gt;POSIX Shell scripts&lt;/h2&gt;&lt;p&gt;Pros: nothing can string together commands better, if you learn 90% of it then you can make pretty nice and expressive programs with it for a certain class of problem, standardized (I do not use bash).&lt;/p&gt;&lt;p&gt;Cons: most people learn only 10% of it and therefore make pretty bad and unintuitive programs with it, not useful for most complex tasks.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Disclaimer: I don’t like the rest of these programming languages and would not use them to solve any problem. If you don’t want your sacred cow gored, leave here.&lt;/p&gt;&lt;h2&gt;C++&lt;/h2&gt;&lt;p&gt;Pros: none&lt;/p&gt;&lt;p&gt;Cons: ill-defined, far too big, &lt;span class=&quot;bullshit &quot;&gt;Object Oriented Programming&lt;/span&gt;, loads of baggage, ecosystem that buys into its crap, enjoyed by bad programmers.&lt;/p&gt;&lt;h2&gt;PHP&lt;/h2&gt;&lt;p&gt;Pros: none&lt;/p&gt;&lt;p&gt;Cons: every PHP programmer is bad at programming, the language is designed to accommodate them with convenient footguns (or faceguns) at every step, and the ecosystem is accordingly bad. No, PHP7 doesn’t fix this. Use a real programming language, jerk.&lt;/p&gt;&lt;h2&gt;Ruby&lt;/h2&gt;&lt;p&gt;Pros: It’s both &lt;span class=&quot;bullshit &quot;&gt;ENTERPRISE&lt;/span&gt; and &lt;span class=&quot;bullshit &quot;&gt;HIP&lt;/span&gt; at the same time, and therefore effective at herding a group of junior to mid-level programmers in a certain direction, namely towards your startup’s exit.&lt;/p&gt;&lt;p&gt;Cons: bloated, awful performance, before Node.js took off this is what all of those programmers used.&lt;/p&gt;&lt;h2&gt;Scala&lt;/h2&gt;&lt;p&gt;Pros: more expressive than Java, useful for &lt;span class=&quot;bullshit &quot;&gt;Big Data&lt;/span&gt; problems.&lt;/p&gt;&lt;p&gt;Cons: Java derivative, type system requires a PhD to comprehend, too siloed from Java, meaning it gets all of the disadvantages of being a Java ecosystem member but few of the advantages. The type system is so needlessly complicated that it basically cripples the language on its own merits alone.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Enough-to-decide/</link>
        
        <pubDate>Sun, 08 Sep 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Enough-to-decide/</guid>
      </item>
    
      <item>
        
        
          <title>Building interactive SSH applications</title>
          <description>
            &lt;p&gt;After the announcement of &lt;a href=&quot;https://drewdevault.com/2019/08/19/Introducing-shell-access-for-builds.html&quot; target=&quot;_blank&quot;&gt;shell access for builds.sr.ht jobs&lt;/a&gt;, a few people sent me some questions, wondering how this sort of thing is done. Writing interactive SSH applications is actually pretty easy, but it does require some knowledge of the pieces involved and a little bit of general Unix literacy.&lt;/p&gt;&lt;p&gt;On the server, there are three steps which you can meddle with using OpenSSH: authentication, the shell session, and the command. The shell is pretty easily manipulated. For example, if you set the user’s login shell to &lt;code&gt;/usr/bin/nethack&lt;/code&gt;, then &lt;a href=&quot;https://www.nethack.org/&quot; target=&quot;_blank&quot;&gt;nethack&lt;/a&gt; will run when they log in. Editing this is pretty straightforward, just pop open &lt;code&gt;/etc/passwd&lt;/code&gt; as root and set their shell to your desired binary. If the user SSHes into your server with a TTY allocated (which is done by default), then you’ll be able to run a curses application or something interactive.&lt;/p&gt;&lt;script
  id=&quot;asciicast-CQ5iaFl8kMnOGV3x0TeI7vfjV&quot;
  src=&quot;https://asciinema.org/a/pafXXANiWHY9MOH2yXdVHHJRd.js&quot; async
&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;i&gt;This article includes third-party JavaScript content from
asciinema.org, a free- and open-source platform that I trust.&lt;/i&gt;&lt;/noscript&gt;
&lt;p&gt;However, a downside to this is that, if you choose a “shell” which does not behave like a shell, it will break when the user passes additional command line arguments, such as &lt;code&gt;ssh user@host ls -a&lt;/code&gt;. To address this, instead of overriding the shell, we can override the &lt;em&gt;command&lt;/em&gt; which is run. The best place to do this is in the user’s &lt;code&gt;authorized_keys&lt;/code&gt; file. Before each line, you can add options which apply to users who log in with that key. One of these options is the “command” option. If you add this to &lt;code&gt;/home/user/.ssh/authorized_keys&lt;/code&gt; instead:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;command=&amp;quot;/usr/bin/nethack&amp;quot; ssh-rsa ... user
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then it’ll use the user’s shell (which should probably be &lt;code&gt;/bin/sh&lt;/code&gt;) to run &lt;code&gt;nethack&lt;/code&gt;, which will work regardless of the command supplied by the user (which is stored into &lt;code&gt;SSH_ORIGINAL_COMMAND&lt;/code&gt; in the environment, should you need it). There are probably some other options you want to set here, as well, for security reasons:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;restrict,pty,command=&amp;quot;...&amp;quot; ssh-rsa ... user
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The full list of options you can set here is available in the &lt;code&gt;sshd(8)&lt;/code&gt; man page. &lt;code&gt;restrict&lt;/code&gt; just turns off most stuff by default, and &lt;code&gt;pty&lt;/code&gt; explicitly re-enables TTY allocation, so that we can do things like curses. This will work if you want to explicitly authorize specific people, one at a time, in your &lt;code&gt;authorized_keys&lt;/code&gt; file, to use your SSH-driven application.  However, there’s one more place where we can meddle: the &lt;code&gt;AuthorizedKeysCommand&lt;/code&gt; in &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;. Instead of having OpenSSH read from the &lt;code&gt;authorized_keys&lt;/code&gt; file in the user’s home directory, it can execute an arbitrary program and read the &lt;code&gt;authorized_keys&lt;/code&gt; file from its stdout. For example, on Sourcehut we use something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;AuthorizedKeysCommand /usr/bin/gitsrht-dispatch &amp;quot;%u&amp;quot; &amp;quot;%h&amp;quot; &amp;quot;%t&amp;quot; &amp;quot;%k&amp;quot;
AuthorizedKeysUser root
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Respectively, these format strings will supply the command with the username attempting login, the user’s home directory, the type of key in use (e.g. &lt;code&gt;ssh-rsa&lt;/code&gt;), and the base64-encoded key itself. More options are available - see &lt;code&gt;TOKENS&lt;/code&gt;, in the &lt;code&gt;sshd_config(8)&lt;/code&gt; man page. The key supplied here can be used to identify the user - on Sourcehut we look up their SSH key in the database. Then you can choose whether or not to admit the user based on any logic of your choosing, and print an appropriate &lt;code&gt;authorized_keys&lt;/code&gt; to stdout. You can also take this opportunity to forward this information along to the command that gets executed, by appending them to the command option or by using the environment options.&lt;/p&gt;&lt;h2&gt;How this works on builds.sr.ht&lt;/h2&gt;&lt;p&gt;We use a somewhat complex system for incoming SSH connections, which I won’t go into here - it’s only necessary to support multiple SSH applications on the same server, like git.sr.ht and builds.sr.ht. For builds.sr.ht, we accept all connections and authenticate later on. This means our AuthorizedKeysCommand is quite simple:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# We just let everyone in at this stage, authentication is done later on.&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;constructor constant variable&quot;&gt;key_type&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;sys&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;argv&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;]
&lt;span class=&quot;constructor constant variable&quot;&gt;b64key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;sys&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;argv&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;]

&lt;span class=&quot;constructor constant variable&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;string&quot;&gt;f&amp;quot;command=&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;buildsrht-shell &amp;apos;&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;b64key&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;,restrict,pty &amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;
    &lt;span class=&quot;string&quot;&gt;f&amp;quot;&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;key_type&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;b64key&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; somebody&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;keys&lt;/span&gt;)
&lt;span class=&quot;constructor constant variable&quot;&gt;sys&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;exit&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command, &lt;code&gt;buildsrht-shell&lt;/code&gt;, does some more interesting stuff. First, the user is told to connect with a command like &lt;code&gt;ssh builds@buildhost connect &amp;lt;job ID&amp;gt;&lt;/code&gt;, so we use the &lt;code&gt;SSH_ORIGINAL_COMMAND&lt;/code&gt; variable to grab the command line they included:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;constructor constant variable&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;environ&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;get&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;SSH_ORIGINAL_COMMAND&amp;quot;&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;constructor constant variable&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;shlex&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;split&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;cmd&lt;/span&gt;)
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;len&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;cmd&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;:
    &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;fail&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;Usage: ssh ... connect &amp;lt;job ID&amp;gt;&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;constructor constant variable&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;cmd&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;in&lt;/span&gt; [&lt;span class=&quot;string&quot;&gt;&amp;quot;connect&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;tail&amp;quot;&lt;/span&gt;]:
    &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;fail&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;Usage: ssh ... connect &amp;lt;job ID&amp;gt;&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;constructor constant variable&quot;&gt;job_id&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;int&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;cmd&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we do some authentication, fetching the job info from the local job runner and checking their key against meta.sr.ht (the authentication service).&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;constructor constant variable&quot;&gt;b64key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;sys&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;argv&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;]

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;get_info&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;job_id&lt;/span&gt;):
    &lt;span class=&quot;constructor constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;requests&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;get&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;f&amp;quot;http://localhost:8080/job/&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;job_id&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;/info&amp;quot;&lt;/span&gt;)
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;r&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;200&lt;/span&gt;:
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;r&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;json&lt;/span&gt;()

&lt;span class=&quot;constructor constant variable&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;get_info&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;job_id&lt;/span&gt;)
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;info&lt;/span&gt;:
    &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;fail&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;No such job found.&amp;quot;&lt;/span&gt;)

&lt;span class=&quot;constructor constant variable&quot;&gt;meta_origin&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;get_origin&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;meta.sr.ht&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;constructor constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;requests&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;get&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;f&amp;quot;&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;meta_origin&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;/api/ssh-key/&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;b64key&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;&lt;/span&gt;)
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;r&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;200&lt;/span&gt;:
    &lt;span class=&quot;constructor constant variable&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;r&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;json&lt;/span&gt;()[&lt;span class=&quot;string&quot;&gt;&amp;quot;owner&amp;quot;&lt;/span&gt;][&lt;span class=&quot;string&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;]
&lt;span class=&quot;keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;r&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;404&lt;/span&gt;:
    &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;fail&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;We don&amp;apos;t recognize your SSH key. Make sure you&amp;apos;ve added it to &amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;string&quot;&gt;f&amp;quot;your account.&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded variable function constructor function_builtin constant&quot;&gt;get_origin&lt;/span&gt;&lt;span class=&quot;string embedded&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string embedded&quot;&gt;&amp;apos;meta.sr.ht&amp;apos;&lt;/span&gt;&lt;span class=&quot;embedded&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;embedded variable constructor constant&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;embedded operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;embedded constant_builtin&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;embedded&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;embedded punctuation_special&quot;&gt;}&lt;/span&gt;/keys&amp;quot;)
&lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;:
    &lt;span class=&quot;variable function constructor function_builtin constant&quot;&gt;fail&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;Temporary authentication failure. Try again later.&amp;quot;&lt;/span&gt;)

&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;[&lt;span class=&quot;string&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt;]:
    &lt;span class=&quot;variable function constructor function_builtin constant&quot;&gt;fail&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;You are not permitted to connect to this job.&amp;quot;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two modes from here on out: connecting and tailing. The former logs into the local build VM, and the latter prints the logs to the terminal. Connecting looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;connect&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;job_id&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;info&lt;/span&gt;):
    &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Opens a shell on the build VM&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable&quot;&gt;limit&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;naturaltime&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;datetime&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;utcnow&lt;/span&gt;() &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;deadline&lt;/span&gt;)
    &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;f&amp;quot;Your VM will be terminated &lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;, or when you log out.&amp;quot;&lt;/span&gt;)
    &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;print&lt;/span&gt;()
    &lt;span class=&quot;constructor constant variable&quot;&gt;requests&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;post&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;f&amp;quot;http://localhost:8080/job/&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;job_id&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;/claim&amp;quot;&lt;/span&gt;)
    &lt;span class=&quot;constructor constant variable&quot;&gt;sys&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;stdout&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;flush&lt;/span&gt;()
    &lt;span class=&quot;constructor constant variable&quot;&gt;sys&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;stderr&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;flush&lt;/span&gt;()
    &lt;span class=&quot;constructor constant variable&quot;&gt;tty&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;open&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;/dev/tty&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;O_RDWR&lt;/span&gt;)
    &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;dup2&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;tty&lt;/span&gt;)
    &lt;span class=&quot;constructor constant variable&quot;&gt;subprocess&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;call&lt;/span&gt;([
        &lt;span class=&quot;string&quot;&gt;&amp;quot;ssh&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;-qt&amp;quot;&lt;/span&gt;,
        &lt;span class=&quot;string&quot;&gt;&amp;quot;-p&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;str&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;info&lt;/span&gt;[&lt;span class=&quot;string&quot;&gt;&amp;quot;port&amp;quot;&lt;/span&gt;]),
        &lt;span class=&quot;string&quot;&gt;&amp;quot;-o&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;UserKnownHostsFile=/dev/null&amp;quot;&lt;/span&gt;,
        &lt;span class=&quot;string&quot;&gt;&amp;quot;-o&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;StrictHostKeyChecking=no&amp;quot;&lt;/span&gt;,
        &lt;span class=&quot;string&quot;&gt;&amp;quot;-o&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;LogLevel=quiet&amp;quot;&lt;/span&gt;,
        &lt;span class=&quot;string&quot;&gt;&amp;quot;build@localhost&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;bash&amp;quot;&lt;/span&gt;
    ])
    &lt;span class=&quot;constructor constant variable&quot;&gt;requests&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;post&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;f&amp;quot;http://localhost:8080/job/&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;job_id&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;/terminate&amp;quot;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is pretty self explanatory, except perhaps for the dup2 - we just open &lt;code&gt;/dev/tty&lt;/code&gt; and make &lt;code&gt;stdin&lt;/code&gt; a copy of it. Some interactive applications misbehave if stdin is not a tty, and this mimics the normal behavior of SSH. Then we log into the build VM over SSH, which with stdin/stdout/stderr rigged up like so will allow the user to interact with the build VM. After that completes, we terminate the VM.&lt;/p&gt;&lt;p&gt;This is mostly plumbing work that just serves to get the user from point A to point B. The tail functionality is more application-like:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;tail&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;job_id&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;info&lt;/span&gt;):
    &lt;span class=&quot;string&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Tails the build logs to stdout&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable&quot;&gt;logs&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;path&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;join&lt;/span&gt;(&lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;cfg&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;builds.sr.ht::worker&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;buildlogs&amp;quot;&lt;/span&gt;), &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;str&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;job_id&lt;/span&gt;))
    &lt;span class=&quot;constructor constant variable&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;subprocess&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;Popen&lt;/span&gt;([&lt;span class=&quot;string&quot;&gt;&amp;quot;tail&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;-f&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;path&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;join&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;logs&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;log&amp;quot;&lt;/span&gt;)])
    &lt;span class=&quot;constructor constant variable&quot;&gt;tasks&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;set&lt;/span&gt;()
    &lt;span class=&quot;constructor constant variable&quot;&gt;procs&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; [&lt;span class=&quot;constructor constant variable&quot;&gt;p&lt;/span&gt;]
    &lt;span class=&quot;comment&quot;&gt;# holy bejeezus this is hacky&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;True&lt;/span&gt;:
        &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;manifest&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;tasks&lt;/span&gt;:
            &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;task&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;tasks&lt;/span&gt;:
                &lt;span class=&quot;keyword&quot;&gt;continue&lt;/span&gt;
            &lt;span class=&quot;constructor constant variable&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;path&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;join&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;logs&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;task&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;name&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;log&amp;quot;&lt;/span&gt;)
            &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;path&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;exists&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;path&lt;/span&gt;):
                &lt;span class=&quot;constructor constant variable&quot;&gt;procs&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;append&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;subprocess&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;Popen&lt;/span&gt;(
                    &lt;span class=&quot;string&quot;&gt;f&amp;quot;tail -f &lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;string embedded constructor constant variable&quot;&gt;shlex&lt;/span&gt;&lt;span class=&quot;string embedded&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;string embedded property variable constructor constant function_method&quot;&gt;quote&lt;/span&gt;&lt;span class=&quot;string embedded&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string embedded variable constructor constant&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;string embedded&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;string embedded punctuation_special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; | &amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt;
                    &lt;span class=&quot;string&quot;&gt;&amp;quot;awk &amp;apos;{ print &lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;[&amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;shlex&lt;/span&gt;.&lt;span class=&quot;property variable constructor constant function_method&quot;&gt;quote&lt;/span&gt;(&lt;span class=&quot;variable constructor constant&quot;&gt;task&lt;/span&gt;.&lt;span class=&quot;property variable constructor constant&quot;&gt;name&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;] &lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt; $0 }&amp;apos;&amp;quot;&lt;/span&gt;,
                    &lt;span class=&quot;variable constructor constant&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant_builtin&quot;&gt;True&lt;/span&gt;))
                &lt;span class=&quot;variable constructor constant&quot;&gt;tasks&lt;/span&gt;.&lt;span class=&quot;property variable constructor constant function_method&quot;&gt;update&lt;/span&gt;({ &lt;span class=&quot;variable constructor constant&quot;&gt;task&lt;/span&gt;.&lt;span class=&quot;property variable constructor constant&quot;&gt;name&lt;/span&gt; })
        &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable function constructor function_builtin constant&quot;&gt;get_info&lt;/span&gt;(&lt;span class=&quot;variable constructor constant&quot;&gt;job_id&lt;/span&gt;)
        &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;:
            &lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;[&lt;span class=&quot;string&quot;&gt;&amp;quot;task&amp;quot;&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;[&lt;span class=&quot;string&quot;&gt;&amp;quot;tasks&amp;quot;&lt;/span&gt;]:
            &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;procs&lt;/span&gt;:
                &lt;span class=&quot;variable constructor constant&quot;&gt;p&lt;/span&gt;.&lt;span class=&quot;property variable constructor constant function_method&quot;&gt;kill&lt;/span&gt;()
            &lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;variable constructor constant&quot;&gt;time&lt;/span&gt;.&lt;span class=&quot;property variable constructor constant function_method&quot;&gt;sleep&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;)

&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;connect&amp;quot;&lt;/span&gt;:
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;[&lt;span class=&quot;string&quot;&gt;&amp;quot;task&amp;quot;&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;[&lt;span class=&quot;string&quot;&gt;&amp;quot;tasks&amp;quot;&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;[&lt;span class=&quot;string&quot;&gt;&amp;quot;status&amp;quot;&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;running&amp;quot;&lt;/span&gt;:
        &lt;span class=&quot;variable function constructor function_builtin constant&quot;&gt;tail&lt;/span&gt;(&lt;span class=&quot;variable constructor constant&quot;&gt;job_id&lt;/span&gt;, &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;)
    &lt;span class=&quot;variable function constructor function_builtin constant&quot;&gt;connect&lt;/span&gt;(&lt;span class=&quot;variable constructor constant&quot;&gt;job_id&lt;/span&gt;, &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;)
&lt;span class=&quot;keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;variable constructor constant&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;tail&amp;quot;&lt;/span&gt;:
    &lt;span class=&quot;variable function constructor function_builtin constant&quot;&gt;tail&lt;/span&gt;(&lt;span class=&quot;variable constructor constant&quot;&gt;job_id&lt;/span&gt;, &lt;span class=&quot;variable constructor constant&quot;&gt;info&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This… I… let’s just pretend you never saw this. And that’s how SSH access to builds.sr.ht works!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Interactive-SSH-programs/</link>
        
        <pubDate>Mon, 02 Sep 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Interactive-SSH-programs/</guid>
      </item>
    
      <item>
        
        
          <title>Shell access for builds.sr.ht CI</title>
          <description>
            &lt;p&gt;Have you ever found yourself staring at a failed CI build, wondering desperately what happened? Or, have you ever needed a fresh machine on-demand to test out an idea in? Have you been working on Linux, but need to test something on OpenBSD? Starting this week, builds.sr.ht can help with all of these problems, because you can now SSH into the build environment.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If you didn’t know, &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;Sourcehut&lt;/a&gt; is the 100% open/libre software forge for hackers, complete with git and Mercurial hosting, CI, mailing lists, and more - with no JavaScript. Try it out!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The next time your build fails on builds.sr.ht, you’ll probably notice the following message:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/thL-.png&quot;&gt;
&lt;figcaption&gt;Screenshot of builds.sr.ht showing a prompt to SSH into the failed build VM and examine it&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;After the build fails, we process everything normally - sending emails, webhooks, and so on - but keep the VM booted for an additional 10 minutes. If you do log in during this window, we keep the VM alive until you log out or until your normal build time limit has elapsed. Once you’ve logged in, you get a shell and can do anything you like, such as examining the build artifacts or tweaking the source and trying again.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ ssh -t builds@azusa.runners.sr.ht connect 81809
Connected to build job #81809 (failed):
https://builds.sr.ht/jobs/~sircmpwn/81809
Your VM will be terminated 4 hours from now, or when you log out.

bash-5.0 $
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also connect to any build over SSH by adding &lt;code&gt;shell: true&lt;/code&gt; to your build manifest. When you do, the VM will be kept alive after all of the tasks have finished (even if it doesn’t fail) so you can SSH in. You can also SSH in before the tasks have finished, and tail the output of the build in your terminal. An example use case might be getting a fresh Alpine environment to test build your package on:&lt;/p&gt;&lt;script
  id=&quot;asciicast-wnLYZwDuvkbIHwgTdmnqtQpXh&quot;
  src=&quot;https://asciinema.org/a/pafXXANiWHY9MOH2yXdVHHJRd.js&quot; async
&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;i&gt;This article includes third-party JavaScript content from
asciinema.org, a free- and open-source platform that I trust.&lt;/i&gt;&lt;/noscript&gt;
&lt;p&gt;This was accomplished with a simple build manifest:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;image: alpine/edge
shell: true
sources:
- https://git.alpinelinux.org/aports
tasks:
- &amp;quot;prep-abuild&amp;quot;: |
    abuild-keygen -an
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since build manifests run normally in advance of your shell login, you can do things like install your preferred editor and dotfiles, pull down your SSH keys through &lt;a href=&quot;https://man.sr.ht/tutorials/builds.sr.ht/using-build-secrets.md&quot; target=&quot;_blank&quot;&gt;build secrets&lt;/a&gt;, or anything else you desire to set up a comfortable working environment.&lt;/p&gt;&lt;p&gt;Furthermore, by leveraging the &lt;a href=&quot;https://man.sr.ht/builds.sr.ht/api.md&quot; target=&quot;_blank&quot;&gt;builds.sr.ht API&lt;/a&gt;, you can write scripts which take advantage of the shell features. Need a NetBSD shell? With a little scripting you can get something like this working:&lt;/p&gt;&lt;script
  id=&quot;asciicast-8etTNE7Ptgmu6hO3cVDlvrAal&quot;
  src=&quot;https://asciinema.org/a/pafXXANiWHY9MOH2yXdVHHJRd.js&quot; async
&gt;&lt;/script&gt;
&lt;p&gt;With experimental multiarch support being rolled out, soon you’ll be just a few keystrokes away from an ARM or PowerPC shell, too.&lt;/p&gt;&lt;p&gt;I want to expand more on SSH access in the future. Stay tuned and &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;let me know&lt;/a&gt; if you have any cool ideas!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Introducing-shell-access-for-builds/</link>
        
        <pubDate>Mon, 19 Aug 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Introducing-shell-access-for-builds/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, August 2019</title>
          <description>
            &lt;p&gt;Outside my window, the morning sun can be seen rising over the land of the rising sun, as I sip from a coffee purchased at the konbini down the street. I almost forgot to order it, as the staffer behind the counter pointed out with a smile and a joke that, having been told in Japanese, mostly went over my head. It’s on this quiet Osaka morning I write today’s status update - there are lots of existing developments to share!&lt;/p&gt;&lt;p&gt;Let’s start with sourcehut news. I deployed a cool feature yesterday - SSH access to builds.sr.ht. You can now SSH into a failed build to examine the failure and investigate the root cause. You can also get a shell on-demand for any build image, including for experimental arm64 support. I’ll be writing a full-length blog post going into detail about this feature later in the week. Additionally, with contributor Ryan Chan’s help, man.sr.ht received a huge overhaul which moved wikis out of man.sr.ht’s dedicated git subsystem and into git.sr.ht repositories, allowing you to make your wiki out of a branch of your main project repo or browse the git data on the web. I’ll be posting more sr.ht news to sr.ht-announce later today if you want to hear more!&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/thL-.png&quot;&gt;
&lt;figcaption&gt;Screenshot of a failed build on builds.sr.ht offering SSH access to the build environment&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc/refs/0.2.0&quot; target=&quot;_blank&quot;&gt;aerc 0.2.0&lt;/a&gt; has been released, which included nearly 200 changes from 34 contributors. I’m grateful to the community for this crazy amount of support - working together we’ll make aerc amazing in no time. Highlights include maildir and sendmail transports, search and filtering, support for &lt;code&gt;mailto:&lt;/code&gt; links, tab completion, and more. We haven’t slowed down since, and the next release already has support lined up for notmuch, more tab completion support, and more features for mail composition. In related news, Greg Kroah-Hartman of Linux kernel fame was kind enough to &lt;a href=&quot;http://www.kroah.com/log/blog/2019/08/14/patch-workflow-with-mutt-2019/&quot; target=&quot;_blank&quot;&gt;write up&lt;/a&gt; details about his email workflow to help guide the direction of aerc. I’ll be writing a follow-up post next week explaining how aerc aims to solve the problems he lays out.&lt;/p&gt;&lt;p&gt;Sway and wlroots continue chugging along as well, with the release of Sway 1.2-rc1 coming earlier this week. This release adds many features from the recent i3 4.17 release, and adds a handful of small features and bug fixes. The corresponding wlroots release will be pretty cool, too, adding support for direct scanout and fixing dozens of bugs. I’d like to draw your attention as well to a cool project from the Sway community: Jason Francis’s &lt;a href=&quot;https://github.com/cyclopsian/wdisplays&quot; target=&quot;_blank&quot;&gt;wdisplays&lt;/a&gt;, a GUI for arranging and configuring displays on wlroots-based desktops. The changes necessary for it to work will land in sway 1.2, and users building from git can try it out today.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/iyU4.png&quot;&gt;&lt;/p&gt;&lt;p&gt;On the DRM leasing and VR for Wayland work I was discussing in the last update, I’m happy to share that I’ve got it working with SteamVR! I’ve written a &lt;a href=&quot;https://drewdevault.com/blog/DRM-leasing-and-VR-for-Wayland/&quot;&gt;detailed blog post&lt;/a&gt; which explains all of the work that went into this project, if you want to learn about it in depth and watch some cool videos summing up the work. There’s still a lot of work to do in negotiating the standardization of new interfaces to support this feature in several projects, but all of the unknowns have been discovered and answered. We will have VR on Wayland soon. I plan on making my way to the &lt;a href=&quot;https://monado.dev/&quot; target=&quot;_blank&quot;&gt;Monado&lt;/a&gt; and &lt;a href=&quot;https://www.khronos.org/openxr&quot; target=&quot;_blank&quot;&gt;OpenXR&lt;/a&gt; to help realize a top-to-bottom free software VR stack designed with Wayland in mind. I’ll also be joining many members of the wlroots gang at &lt;a href=&quot;https://xdc2019.x.org/&quot; target=&quot;_blank&quot;&gt;XDC&lt;/a&gt; in October, where I hope to meet the people working on OpenXR.&lt;/p&gt;&lt;p&gt;I’ve also invested more time into my Wayland book, because I’ve realized that at my current pace it won’t be done any time soon. It’s now about half complete and I’ve picked up the pace considerably. If you’re interested in helping review the drafts, please let me know!&lt;/p&gt;&lt;p&gt;That’s all for today. Thank you for your continued support!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-August-2019/</link>
        
        <pubDate>Thu, 15 Aug 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-August-2019/</guid>
      </item>
    
      <item>
        
        
          <title>DRM leasing: VR for Wayland</title>
          <description>
            &lt;p&gt;As those who read my &lt;a href=&quot;https://drewdevault.com/blog/Status-update-July-2019/&quot;&gt;status updates&lt;/a&gt; have been aware, recently I’ve been working on bringing VR to Wayland (and vice versa). The deepest and most technical part of this work is &lt;em&gt;DRM leasing&lt;/em&gt; (Direct Rendering Manager, &lt;em&gt;not&lt;/em&gt; Digital Restrictions Management), and I think it’d be good to write in detail about what’s involved in this part of the effort. This work has been sponsored by &lt;a href=&quot;https://status.im/&quot; target=&quot;_blank&quot;&gt;Status.im&lt;/a&gt;, as part of an effort to build a comprehensive Wayland-driven VR workspace. When we got started, most of the plumbing was missing for VR headsets to be useful on Wayland, so this has been my focus for a while. The result of this work is summed up in this crappy handheld video:&lt;/p&gt;&lt;video src=&quot;https://yukari.sr.ht/steamvr.webm&quot; controls&gt;
  Your web browser does not support the webm video codec. Please consider using
  web browsers that support free and open standards.
&lt;/video&gt;
&lt;p&gt;Keith Packard, a long time Linux graphics developer, &lt;a href=&quot;https://keithp.com/blogs/DRM-lease/&quot; target=&quot;_blank&quot;&gt;wrote several blog posts documenting his work implementing this feature for X11&lt;/a&gt;. My journey was somewhat similar, though thanks to his work I was able to save a lot of time. The rub of this idea is that the Wayland compositor, the DRM (Direct Rendering Manager) master, can “lease” some of its resources to a client so they can drive your display directly. DRM is the kernel subsystem we use for enumerating and setting modes, allocating pixel buffers, and presenting them in sync with the display’s refresh rate. For a number of reasons, minimizing latency being an important one, VR applications prefer to do these tasks directly rather than be routed through the display server like most applications are. The main tasks for implementing this for Wayland were:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Draft a &lt;a href=&quot;https://lists.freedesktop.org/archives/wayland-devel/2019-July/040768.html&quot; target=&quot;_blank&quot;&gt;protocol extension&lt;/a&gt; for issuing DRM leases&lt;/li&gt;&lt;li&gt;Write implementations for &lt;a href=&quot;https://github.com/swaywm/wlroots/pull/1730&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt; and &lt;a href=&quot;https://github.com/swaywm/sway/pull/4289&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Get a &lt;a href=&quot;https://git.sr.ht/~sircmpwn/kmscube&quot; target=&quot;_blank&quot;&gt;simple test client&lt;/a&gt; working&lt;/li&gt;&lt;li&gt;Draft a Vulkan extension for leasing via Wayland&lt;/li&gt;&lt;li&gt;Write an implementation for &lt;a href=&quot;https://gitlab.freedesktop.org/mesa/mesa/merge_requests/1509&quot; target=&quot;_blank&quot;&gt;Mesa’s Vulkan WSI implementation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Get a more complex &lt;a href=&quot;https://git.sr.ht/~sircmpwn/xrgears&quot; target=&quot;_blank&quot;&gt;Vulkan test client&lt;/a&gt; working&lt;/li&gt;&lt;li&gt;Add support to &lt;a href=&quot;https://gitlab.freedesktop.org/xorg/xserver/merge_requests/248&quot; target=&quot;_blank&quot;&gt;Xwayland&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Let’s break down exactly what was necessary for each of these steps.&lt;/p&gt;&lt;h2&gt;Wayland protocol extension&lt;/h2&gt;&lt;p&gt;Writing a protocol extension was the first order of business. There was an &lt;a href=&quot;https://lists.freedesktop.org/archives/wayland-devel/2018-January/036652.html&quot; target=&quot;_blank&quot;&gt;earlier attempt&lt;/a&gt; which petered off in January. I started with this, by cleaning it up based on my prior experience writing protocols, normalizing much of the terminology and style, and cleaning up the state management. After some initial rounds of review, there were some questions to answer. The most important ones were:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;How do we identify the display? Should we send the EDID, which may be bigger than the maximum size of a Wayland message?&lt;/li&gt;&lt;li&gt;Are there security concerns? Could malicious clients read from framebuffers they weren’t given a lease for?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The EDID I ended up sending in a side channel (file descriptor to shared memory), and the latter was proven to be a non-issue by writing a malicious client and demonstrating that the kernel rejects its attempts to do evil.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;edid&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;edid&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
    The compositor may send this event once the connector is created to
    provide a file descriptor which may be memory-mapped to read the
    connector&amp;apos;s EDID, to assist in selecting the correct connectors
    for lease. The fd must be mapped with MAP_PRIVATE by the recipient.

    Note that not all displays have an EDID, and this event will not be
    sent in such cases.
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;edid&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;EDID file descriptor&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;EDID size, in bytes&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few more changes would happen to this protocol in the following weeks, but this was good enough to move on to…&lt;/p&gt;&lt;h2&gt;wlroots &amp; sway implementation&lt;/h2&gt;&lt;p&gt;After a chat with Scott Anderson (the maintainer of DRM support in wlroots) and thanks to his timely refactoring efforts, the stage was well set for introducing this feature to wlroots. I had a good idea of how it would take shape. &lt;a href=&quot;https://github.com/swaywm/wlroots/pull/1730/files#diff-77b17feac8a8af251811a20e5b9bbdd1&quot; target=&quot;_blank&quot;&gt;Half of the work&lt;/a&gt; - the state machine which maintains the server-side view of the protocol - is well trodden ground and was fairly easy to put together. Despite being a well-understood problem in the wlroots codebase, these state machines are always a bit tedious to implement correctly, and I was still to flushing out bugs well into the remainder of this workstream.&lt;/p&gt;&lt;p&gt;The other half of this work was in &lt;a href=&quot;https://github.com/swaywm/wlroots/pull/1730/files#diff-8b05a774317ee8e87d51422170f82d4b&quot; target=&quot;_blank&quot;&gt;the DRM subsystem&lt;/a&gt;. We decided that we’d have leased connectors appear “destroyed” to the compositor, and thus the compositor would have an opportunity to clean it up and stop using them, similar to the behavior of when an output is hotplugged. Further changes were necessary to have the DRM internals elegantly carry around some state for the leased connector and avoid using the connector itself, as well as dealing with the termination of the lease (either by the client or by the compositor). With all of this in place, it’s a &lt;a href=&quot;https://github.com/swaywm/wlroots/pull/1730/files#diff-8b05a774317ee8e87d51422170f82d4bR1601&quot; target=&quot;_blank&quot;&gt;simple matter&lt;/a&gt; to enumerate the DRM object IDs for all of the resources we intend to lease and issue the lease itself.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nobjects&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nconns&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_drm_connector&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conns&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;WLR_DRM_CONN_LEASED&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;nobjects&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;
		&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;/* connector */&lt;/span&gt;
		&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;/* crtc */&lt;/span&gt;
		&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;/* primary plane */&lt;/span&gt;
		&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;cursor&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt; ? &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; : &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) &lt;span class=&quot;comment&quot;&gt;/* cursor plane */&lt;/span&gt;
		&lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;num_overlays&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;/* overlay planes */&lt;/span&gt;
}
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;nobjects&lt;/span&gt; &amp;lt;= &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
	&lt;span class=&quot;constant variable function&quot;&gt;wlr_log&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;WLR_ERROR&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;Attempted DRM lease with &amp;lt;= 0 objects&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;span class=&quot;constant variable function&quot;&gt;wlr_log&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;WLR_DEBUG&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;Issuing DRM lease with the %d objects:&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;nobjects&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;objects&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;nobjects&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nconns&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_drm_connector&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conns&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;objects&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;objects&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;objects&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;primary&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;wlr_log&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;WLR_DEBUG&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;connector: %d crtc: %d primary plane: %d&amp;quot;&lt;/span&gt;,
			&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;id&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;id&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;primary&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;id&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;cursor&lt;/span&gt;) {
		&lt;span class=&quot;constant variable function&quot;&gt;wlr_log&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;WLR_DEBUG&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;cursor plane: %d&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;id&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;objects&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;num_overlays&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
		&lt;span class=&quot;constant variable function&quot;&gt;wlr_log&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;WLR_DEBUG&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;+%zd overlay planes:&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;num_overlays&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;num_overlays&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt;) {
		&lt;span class=&quot;constant variable&quot;&gt;objects&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;overlays&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable function&quot;&gt;wlr_log&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;WLR_DEBUG&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;\toverlay plane: %d&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;overlays&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;k&lt;/span&gt;])&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
}
&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lease_fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;drmModeCreateLease&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;,
		&lt;span class=&quot;constant variable&quot;&gt;objects&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;nobjects&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;lessee_id&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;lease_fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lease_fd&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;span class=&quot;constant variable function&quot;&gt;wlr_log&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;WLR_DEBUG&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;Issued DRM lease %d&amp;quot;&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lessee_id&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nconns&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_drm_connector&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conns&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;lessee_id&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lessee_id&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;lessee_id&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lessee_id&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;WLR_DRM_CONN_LEASED&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;lease_terminated_cb&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lease_terminated_cb&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;lease_terminated_data&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lease_terminated_data&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;wlr_output_destroy&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;output&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lease_fd&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/swaywm/sway/pull/4289&quot; target=&quot;_blank&quot;&gt;sway implementation&lt;/a&gt; is very simple. I added a note in wlroots which exposes whether or not an output is considered “non-desktop” (a property which is set for most VR headsets), then sway just rigs up the lease manager and offers all non-desktop outputs for lease.&lt;/p&gt;&lt;h2&gt;kmscube&lt;/h2&gt;&lt;p&gt;Testing all of this required the use of a simple test client. During his earlier work, Keith wrote some patches on top of &lt;a href=&quot;https://gitlab.freedesktop.org/mesa/kmscube/&quot; target=&quot;_blank&quot;&gt;kmscube&lt;/a&gt;, a simple Mesa demo which renders a spinning cube directly via DRM/KMS/GBM. A &lt;a href=&quot;https://git.sr.ht/~sircmpwn/kmscube/commit/60d89ef1d9304427a1289174d9a311ab06e39b44&quot; target=&quot;_blank&quot;&gt;few simple tweaks&lt;/a&gt; was suitable to get this working through my protocol extension, and for the first time I saw something rendered on my headset through sway!&lt;/p&gt;&lt;video src=&quot;https://yukari.sr.ht/vr.webm&quot; controls&gt;
  Your web browser does not support the webm video codec. Please consider using
  web browsers that support free and open standards.
&lt;/video&gt;
&lt;h2&gt;Vulkan&lt;/h2&gt;&lt;p&gt;Vulkan has a subsystem called WSI - Window System Integration - which handles the linkage between Vulkan’s rendering process and the underlying window system, such as Wayland, X11, or win32. Keith added an extension to this system called &lt;a href=&quot;https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VK_EXT_acquire_xlib_display&quot; target=&quot;_blank&quot;&gt;VK_EXT_acquire_xlib_display&lt;/a&gt;, which lives on top of &lt;a href=&quot;https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VK_EXT_direct_mode_display&quot; target=&quot;_blank&quot;&gt;VK_EXT_direct_mode_display&lt;/a&gt;, a system for driving displays directly with Vulkan. As the name implies, this system is especially X11-specific, so I’ve drafted my own VK extension for Wayland: VK_EXT_acquire_wl_display. This is the crux of it:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;successcodes&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;VK_SUCCESS&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;errorcodes&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;VK_ERROR_INITIALIZATION_FAILED&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;proto&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;VkResult&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;vkAcquireWaylandDisplayEXT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;proto&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;VkPhysicalDevice&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;physicalDevice&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;struct &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;wl_display&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;* &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;struct &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;zwp_drm_lease_manager_v1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;* &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;manager&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;nConnectors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;VkWaylandLeaseConnectorEXT&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;* &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;pConnectors&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I chose to leave it up to the user to enumerate the leasable connectors from the Wayland protocol, then populate these structs with references to the connectors they want to lease:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;VkWaylandLeaseConnectorEXT&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;member&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;struct &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;zwp_drm_lease_connector_v1&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;* &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;pConnectorIn&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;member&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;member&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;VkDisplayKHR&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt; &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;displayOut&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;member&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, this was the result of some iteration and design discussions with other folks knowledgable in these topics. I owe special thanks to Daniel Stone for sitting down with me (figuratively, on IRC) and going over ideas for how to design the Vulkan API. Armed with this specification, I now needed a Vulkan driver which supported it.&lt;/p&gt;&lt;h2&gt;Implementing the VK extension in Mesa&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://www.mesa3d.org/&quot; target=&quot;_blank&quot;&gt;Mesa&lt;/a&gt; is the premier free software graphics suite powering graphics on Linux and other operating systems. It includes an implementation of OpenGL and Vulkan for several GPU vendors, and is the home of the userspace end of AMDGPU, Intel, nouveau, and other graphics drivers. A specification is nothing without its implementation, so I set out to implementing this extension for Mesa. In the end, it turned out to be much simpler than the corresponding X version. This is the complete code for the WSI part of this feature:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;drm_lease_handle_lease_fd&lt;/span&gt;(
      &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;,
      &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;zwp_drm_lease_v1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;zwp_drm_lease_v1&lt;/span&gt;,
      &lt;span class=&quot;type&quot;&gt;int32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;leased_fd&lt;/span&gt;)
{
   &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wsi_display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;leased_fd&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;drm_lease_handle_finished&lt;/span&gt;(
      &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;,
      &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;zwp_drm_lease_v1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;zwp_drm_lease_v1&lt;/span&gt;)
{
   &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wsi_display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
      &lt;span class=&quot;constant variable function&quot;&gt;close&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   }
}

&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;zwp_drm_lease_v1_listener&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;drm_lease_listener&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; {
   &lt;span class=&quot;constant variable&quot;&gt;drm_lease_handle_lease_fd&lt;/span&gt;,
   &lt;span class=&quot;constant variable&quot;&gt;drm_lease_handle_finished&lt;/span&gt;,
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment&quot;&gt;/* VK_EXT_acquire_wl_display */&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;VkResult&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;wsi_acquire_wl_display&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;VkPhysicalDevice&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;physical_device&lt;/span&gt;,
                       &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wsi_device&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;wsi_device&lt;/span&gt;,
                       &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;display&lt;/span&gt;,
                       &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;zwp_drm_lease_manager_v1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;manager&lt;/span&gt;,
                       &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nConnectors&lt;/span&gt;,
                       &lt;span class=&quot;type&quot;&gt;VkWaylandLeaseConnectorEXT&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;connectors&lt;/span&gt;)
{
   &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wsi_display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
      (&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wsi_display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;) &lt;span class=&quot;constant variable&quot;&gt;wsi_device&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;wsi&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;VK_ICD_WSI_PLATFORM_DISPLAY&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;comment&quot;&gt;/* XXX no support for mulitple leases yet */&lt;/span&gt;
   &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt; &amp;gt;= &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)
      &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;VK_ERROR_INITIALIZATION_FAILED&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;comment&quot;&gt;/* XXX no support for mulitple connectors yet */&lt;/span&gt;
   &lt;span class=&quot;comment&quot;&gt;/* The solution will eventually involve adding a listener to each
    * connector, round tripping, and matching EDIDs once the lease is
    * granted. */&lt;/span&gt;
   &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;nConnectors&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;)
      &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;VK_ERROR_INITIALIZATION_FAILED&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;zwp_drm_lease_request_v1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;lease_request&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;constant variable function&quot;&gt;zwp_drm_lease_manager_v1_create_lease_request&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;manager&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nConnectors&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;) {
      &lt;span class=&quot;constant variable function&quot;&gt;zwp_drm_lease_request_v1_request_connector&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;lease_request&lt;/span&gt;,
                                                 &lt;span class=&quot;constant variable&quot;&gt;connectors&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;pConnectorIn&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   }

   &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;zwp_drm_lease_v1&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;drm_lease&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;constant variable function&quot;&gt;zwp_drm_lease_request_v1_submit&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;lease_request&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;constant variable function&quot;&gt;zwp_drm_lease_request_v1_destroy&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;lease_request&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;constant variable function&quot;&gt;zwp_drm_lease_v1_add_listener&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;drm_lease&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;drm_lease_listener&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;constant variable function&quot;&gt;wl_display_roundtrip&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;display&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)
      &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;VK_ERROR_INITIALIZATION_FAILED&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nconn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;type&quot;&gt;drmModeResPtr&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;drmModeGetResources&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;type&quot;&gt;drmModeObjectListPtr&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lease&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;drmModeGetLease&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;wsi&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;count_connectors&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;) {
      &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lease&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;) {
         &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;connectors&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;lease&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;objects&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;j&lt;/span&gt;]) {
            &lt;span class=&quot;keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
         }
         &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wsi_display_connector&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;connector&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;constant variable function&quot;&gt;wsi_display_get_connector&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;wsi_device&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;connectors&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;])&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
         &lt;span class=&quot;comment&quot;&gt;/* TODO: Match EDID with requested connector */&lt;/span&gt;
         &lt;span class=&quot;constant variable&quot;&gt;connectors&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;nconn&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;displayOut&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
            &lt;span class=&quot;constant variable function&quot;&gt;wsi_display_connector_to_handle&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;connector&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
         &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;nconn&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
      }
   }
   &lt;span class=&quot;constant variable function&quot;&gt;drmModeFreeResources&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;VK_SUCCESS&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rigging it up to each driver’s WSI shim is pretty straightforward from this point. I only did it for radv - AMD’s Vulkan driver (cause that’s the hardware I was using at the time) - but the rest should be trivial to add. Equipped with a driver in hand, it’s time to make a Real VR Application work on Wayland.&lt;/p&gt;&lt;h2&gt;xrgears&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://gitlab.com/lubosz/xrgears&quot; target=&quot;_blank&quot;&gt;xrgears&lt;/a&gt; is another simple demo application like kmscube - but designed to render a VR scene. It leverages Vulkan and &lt;a href=&quot;http://www.openhmd.net/&quot; target=&quot;_blank&quot;&gt;OpenHMD&lt;/a&gt; (Open Head Mounted Display) to display this scene and stick the camera to your head. With the Vulkan extension implemented, it was a fairly simple matter to &lt;a href=&quot;https://git.sr.ht/~sircmpwn/xrgears/commit/41ef1d1dfe3e56766d1f8b72b335567eb7842d04&quot; target=&quot;_blank&quot;&gt;rig up a Wayland backend&lt;/a&gt;. The result:&lt;/p&gt;&lt;video src=&quot;https://yukari.sr.ht/xrgears.webm&quot; controls&gt;
  Your web browser does not support the webm video codec. Please consider using
  web browsers that support free and open standards.
&lt;/video&gt;
&lt;h2&gt;Xwayland&lt;/h2&gt;&lt;p&gt;The final step was to integrate this extension with Xwayland, so that X applications which took advantage of Keith’s work would work via Xwayland. This ended up being more difficult than I expected for one reason in particular: modes. Keith’s Vulkan extension is designed in two steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Convert an RandR output into a VkDisplayKHR&lt;/li&gt;&lt;li&gt;Acquire a lease for a set of VkDisplayKHRs&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Between these steps, you can query the modes (available resolutions and refresh rates) of the display. However, the Wayland protocol I designed does not let you query modes until &lt;em&gt;after&lt;/em&gt; you get the DRM handle, at which point you should query them through DRM, thus reducing the number of sources of truth and simplifying things considerably. This is arguably a design misstep in the original Vulkan extension, but it’s shipped in a lot of software and is beyond fixing. So how do we deal with it?&lt;/p&gt;&lt;p&gt;One way (which was suggested at one point) would be to change the protocol to include the relevant mode information, so that Xwayland could populate the RandR modes from it. I found this distasteful, because it was making the protocol more complex for the sake of a legacy system. Another option would be to make a second protocol which includes this extra information especially for Xwayland, but this also seemed like a compromise that compositors would rather not make. Yet another option would be to have Xwayland request a lease with zero objects and scan connectors itself, but zero-object leases are not possible.&lt;/p&gt;&lt;p&gt;The option I ended up going with is to have Xwayland open the DRM device itself and scan connectors there. This is less palatable because (1) we can’t be sure which DRM device is correct, and (2) we can’t be sure Xwayland will have permission to read it. We’re still not sure how best to solve this in the long term. As it stands, this approach is sufficient to get it working in the common case. The code looks something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;RRModePtr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;xwl_get_rrmodes_from_connector_id&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;connector_id&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;nmode&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;npref&lt;/span&gt;)
{
    &lt;span class=&quot;type&quot;&gt;drmDevicePtr&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;devices&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;drmModeConnectorPtr&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;drmModeModeInfoPtr&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;RRModePtr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;rrmodes&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;drm&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pref&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;nmode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;npref&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;/* TODO: replace with zero-object lease once kernel supports them */&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable function&quot;&gt;drmGetDevices2&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;DRM_NODE_PRIMARY&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;devices&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;operator&quot;&gt;||&lt;/span&gt; !&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;devices&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;nodes&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]) {
        &lt;span class=&quot;constant variable function&quot;&gt;ErrorF&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;Failed to enumerate DRM devices&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
    &lt;span class=&quot;constant variable&quot;&gt;drm&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;open&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;devices&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;nodes&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;], &lt;span class=&quot;constant variable&quot;&gt;O_RDONLY&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable function&quot;&gt;drmFreeDevices&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;devices&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;drmModeGetConnector&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;drm&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;connector_id&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (!&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;) {
        &lt;span class=&quot;constant variable function&quot;&gt;close&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;drm&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable function&quot;&gt;ErrorF&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;drmModeGetConnector failed&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
    &lt;span class=&quot;constant variable&quot;&gt;rrmodes&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;xallocarray&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;count_modes&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;RRModePtr&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (!&lt;span class=&quot;constant variable&quot;&gt;rrmodes&lt;/span&gt;) {
        &lt;span class=&quot;constant variable function&quot;&gt;close&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;drm&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable function&quot;&gt;ErrorF&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;Failed to allocate connector modes&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }

    &lt;span class=&quot;comment&quot;&gt;/* This spaghetti brought to you courtesey of xf86RandrR12.c
     * It adds preferred modes first, then non-preferred modes */&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;pref&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pref&lt;/span&gt; &amp;gt;= &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pref&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;--&lt;/span&gt;) {
        &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;count_modes&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;) {
            &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;modes&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; ((&lt;span class=&quot;constant variable&quot;&gt;pref&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; ((&lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;DRM_MODE_TYPE_PREFERRED&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)) {
                &lt;span class=&quot;type&quot;&gt;xRRModeInfo&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;type&quot;&gt;RRModePtr&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rrmode&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;nameLength&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;strlen&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;hdisplay&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;dotClock&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;hSyncStart&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;hsync_start&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;hSyncEnd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;hsync_end&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;hTotal&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;htotal&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;hSkew&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;hskew&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;vdisplay&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;vSyncStart&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;vsync_start&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;vSyncEnd&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;vsync_end&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;vTotal&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;vtotal&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;modeFlags&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

                &lt;span class=&quot;constant variable&quot;&gt;rrmode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;RRModeGet&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;modeInfo&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;kmode&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;rrmode&lt;/span&gt;) {
                    &lt;span class=&quot;constant variable&quot;&gt;rrmodes&lt;/span&gt;[&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;nmode&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rrmode&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;nmode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;nmode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;npref&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;npref&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;pref&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
                }
            }
        }
    }

    &lt;span class=&quot;constant variable function&quot;&gt;close&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;drm&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;rrmodes&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A simple update to the Wayland protocol was necessary to add the &lt;code&gt;CONNECTOR_ID&lt;/code&gt; atom to the RandR output, which is used by Mesa’s Xlib WSI code for acquiring the display, and was reused here to line up a connector offered by the Wayland compositor with a connector found in the kernel. The &lt;a href=&quot;https://gitlab.freedesktop.org/xorg/xserver/merge_requests/248&quot; target=&quot;_blank&quot;&gt;rest of the changes&lt;/a&gt; were pretty simple, and the result is that SteamVR works, capping everything off nicely:&lt;/p&gt;&lt;video src=&quot;https://yukari.sr.ht/steamvr.webm&quot; controls&gt;
  Your web browser does not support the webm video codec. Please consider using
  web browsers that support free and open standards.
&lt;/video&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/DRM-leasing-and-VR-for-Wayland/</link>
        
        <pubDate>Fri, 09 Aug 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/DRM-leasing-and-VR-for-Wayland/</guid>
      </item>
    
      <item>
        
        
          <title>FOSS contributor tracks</title>
          <description>
            &lt;p&gt;Just like many companies have different advancement tracks for their employees (for example, a management track and an engineering track), similar concepts exist in free software projects. One of the roles of a maintainer is to help contributors develop into the roles which best suit them. I’d like to explain what this means to me in my role as a maintainer of several projects, though I should mention upfront that I’m just some guy and, while I can explain what has and hasn’t worked for me, I can’t claim to have all of the answers. People are hard.&lt;/p&gt;&lt;p&gt;There are lots of different tasks which need doing on a project. A few which come up fairly often include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;End-user support&lt;/li&gt;&lt;li&gt;Graphic design&lt;/li&gt;&lt;li&gt;Marketing&lt;/li&gt;&lt;li&gt;Release planning&lt;/li&gt;&lt;li&gt;Reviewing code&lt;/li&gt;&lt;li&gt;Translations&lt;/li&gt;&lt;li&gt;Triaging tickets&lt;/li&gt;&lt;li&gt;Writing code&lt;/li&gt;&lt;li&gt;Writing documentation&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Within these tasks there’s room still for more specialization - different modules have different maintainers, each contributor’s skills may be applicable to different parts of the codebase, some people may like blogging about the project where others like representing the project at conferences, and so on. To me, one of my most important jobs is to figure out these relationships between tasks and people.&lt;/p&gt;&lt;p&gt;There are several factors that go into this. Keeping an eye on code reviews, social channels, etc, gives you a good pulse on what people are good at now. Talking with them directly and discussing possible future work is a good way to understand what they want to work on. I also often consider what they could be good at but don’t have exposure to yet, and encourage them to take on more of these tasks. The most common case where I try to get people to branch out is code review - once they’ve contributed to a module they’re put on the shortlist for reviewers for future changes to nearby code. Don’t be afraid to take risks - a few bugs is a small price to pay for an experienced contributor.&lt;/p&gt;&lt;p&gt;This also touches on another key part of this work - fostering collaboration. For example, if someone is taking on a cross-cutting task, I’ll give them the names of experts on all of the affected modules so they can ask questions and seek buy-in on their approach. Many developers aren’t interested in end-user support, so getting people who are interested in this to bubble up technical issues when they’re found is helpful as well.&lt;/p&gt;&lt;p&gt;The final step is to gradually work your way out of the machine. Just like you onboard someone with feature development or code review, you can onboard people with maintainer tasks. If someone asks you to connect them to experts on some part of the code, defer to a senior contributor - who has likely asked you the same question at some point. Ask a contributor to go over the shortlog and prepare a draft for the next release notes. Pull a trusted contributor aside and ask them what they think needs to be improved in the project - then ask them to make those improvements, and equip them with any tools they need to accomplish it.&lt;/p&gt;&lt;p&gt;One role I tend to reserve for myself is conflict prevention and moderation. I keep a light watch on collaboration channels and periodically sync with major contributors, keeping a pulse for the flow of information through the project. When arguments start brewing or things start getting emotional, I try to notice early and smooth things over before they get heated. At an impasse, I’ll make a final judgement call on a feature, design decision, or whatever else. By making the decision, I aim to make it neither party’s fault that someone didn’t get their way. Instead, I point any blame at myself, and rely on the mutual trust between myself and the contributors to see the decision through amicably. When this works correctly, it can help preserve a good relationship between each party.&lt;/p&gt;&lt;p&gt;If you’re lucky, the end result is a project which can grow arbitrarily large, with contributors bringing a variety of skills to support each other at every level and enjoy the work they’re doing. The bus factor is low and everyone maintains a healthy and productive relationship with the project - yourself included.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/FOSS-contributor-tracks/</link>
        
        <pubDate>Mon, 29 Jul 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/FOSS-contributor-tracks/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, July 2019</title>
          <description>
            &lt;p&gt;Today I received the keys to my new apartment, which by way of not being directly in the middle of the city&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; saves me a decent chunk of money - and allows me to proudly announce that I have officially broken even on doing free software full time! I owe a great deal of thanks to all of you who have &lt;a href=&quot;https://drewdevault.com/donate&quot; target=&quot;_blank&quot;&gt;donated to support my work&lt;/a&gt; or purchased a paid &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut&lt;/a&gt; account. I’ve dreamed of sustainably working on free software for a long, long time, and I’m very grateful for all of your support in helping realize that dream. Now let me share with you what your money has bought over the past month!&lt;/p&gt;&lt;p&gt;First, my &lt;a href=&quot;https://drewdevault.com/make-a-blog&quot; target=&quot;_blank&quot;&gt;make a blog&lt;/a&gt; offer has closed for the time being, and the world is now 13 blogs richer for it. Be sure to check them out! I have also started a mailing list for tech writers: the &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/free-writers-club&quot; target=&quot;_blank&quot;&gt;free writers club&lt;/a&gt;, which I encourage anyone using free software to blog about technology to join for editorial advice, software recommendations, and periodic reminders to keep writing. The offer to get paid for your own new blog will reopen in the future, keep an eye out!&lt;/p&gt;&lt;p&gt;As far as projects are concerned, lots of good stuff this month. aerc has been making excellent progress. We just pulled in the first batch of patches adding maildir support, and will soon have sendmail and mbox support as well. We’ve also begun on mouse support, and you can now click to switch between tabs. The initial patches for tab completion have also been added. Additional changes include an :unsubscribe command to unsubscribe from marketing emails and mailing lists, basic search functionality, OAuth IMAP authentication, changing config options at runtime, and DNS lookups to complete your settings in the new account wizard more quickly. Building more upon these features, and a handler for mailto links, are the main blockers for aerc 0.2.0.&lt;/p&gt;&lt;p&gt;In Wayland news, VR work continues. I’ve taken on the goal of implementing DRM leasing for Wayland, which will allow VR applications to take exclusive control over the headset’s graphical resources from Wayland compositor. A similar technology exists for X11, and I’ve written a Wayland protocol for the same purpose on Wayland. I’ve also written a Vulkan extension to utilize this protocol in Vulkan’s WSI layer. I’ve written implementations of these for wlroots, sway, mesa, and the radv (AMD) Vulkan driver. The result: a working VR demo on Sway (audio warning):&lt;/p&gt;&lt;p&gt;&lt;video controls&gt;
&lt;source src=&quot;https://yukari.sr.ht/xrgears.webm&quot;&gt;
&lt;/video&gt;&lt;/p&gt;&lt;p&gt;There’s still some details to sort out on the standardization of these extensions, which are under discussion now. In the coming weeks I hope to have an implementation for Xwayland (which will get working games based on Steam’s OpenVR runtime), and get a proof-of-concept of a VR-driven Wayland compositor based on the demo shown in the previous status update. Exciting stuff!&lt;/p&gt;&lt;p&gt;I’ve also had time to write a few more chapters for my Wayland book, which I’ll be speeding up my work on. I’ll soon be leaving for an extended trip to Japan, and on these grueling flights I’ll have plenty of time to work on it. In additional Wayland news, we’ve been chugging along with small bugfixes and improvements to wlroots and sway, and implementing more plumbing work to round out our implementation of everything. Our work continues to evolve into the most robust Wayland implementation available today, and I can only see it getting stronger.&lt;/p&gt;&lt;p&gt;On SourceHut, I have plenty of developments to share, but will leave the details for the &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-announce&quot; target=&quot;_blank&quot;&gt;sr.ht-announce mailing list&lt;/a&gt;. The most exciting news is that &lt;a href=&quot;https://alpinelinux.org&quot; target=&quot;_blank&quot;&gt;Alpine Linux&lt;/a&gt;, my favorite Linux distribution, has completed their mailing list infrastructure migration to &lt;a href=&quot;https://lists.alpinelinux.org&quot; target=&quot;_blank&quot;&gt;their own lists.sr.ht instance&lt;/a&gt;! I’ve also been hard at work expanding lists.sr.ht’s capabilities to this end. The other big piece of news was announced on my blog last week: &lt;a href=&quot;https://drewdevault.com/2019/07/08/Announcing-annotations-for-sourcehut.html&quot; target=&quot;_blank&quot;&gt;code annotations&lt;/a&gt;. All of our services have also been upgraded to Alpine 3.10, and the Alpine mirror reorganized a bit to make future upgrades smooth.  There’s all sorts of other goodies to share, but I’ll leave the rest for the sr.ht-announce post later today.&lt;/p&gt;&lt;p&gt;All sorts of other little things have gotten done, like sending patches upstream for kmscube fixes, minor improvements to scdoc, writing a new build system for mrsh, improvements to openring… but I’m running out of patience and I imagine you are, too. Again I’m eternally grateful for your support: thank you. I’ll see you again for the next status update, same time next month!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-July-2019/</link>
        
        <pubDate>Mon, 15 Jul 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-July-2019/</guid>
      </item>
    
      <item>
        
        
          <title>Announcing code annotations for SourceHut</title>
          <description>
            &lt;p&gt;Today I’m happy to announce that code annotations are now available for &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut&lt;/a&gt;! &lt;img style=&quot;display: inline; height: 1.2rem&quot;
src=&quot;/img/party.png&quot; /&gt; These allow you to decorate your code with arbitrary links and markdown. The end result looks something like this:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;NOTICE&lt;/strong&gt;: Annotations were ultimately removed from sourcehut.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/w767.png&quot;&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut&lt;/a&gt; is the “hacker’s forge”, a 100% open-source platform for hosting Git &amp; Mercurial repos, bug trackers, mailing lists, continuous integration, and more. No JavaScript required!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The annotations shown here are sourced from a JSON file which you can generate and upload during your CI process. It looks something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;json&quot;&gt;{
  &lt;span class=&quot;string&quot;&gt;&amp;quot;98bc0394a2f15171fb113acb5a9286a7454f22e7&amp;quot;&lt;/span&gt;: [
    {
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;type&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;markdown&amp;quot;&lt;/span&gt;,
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;lineno&amp;quot;&lt;/span&gt;: &lt;span class=&quot;number&quot;&gt;33&lt;/span&gt;,
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;title&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;1 reference&amp;quot;&lt;/span&gt;,
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;content&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;- [../main.c:123](https://example.org)&amp;quot;&lt;/span&gt;
    },
    {
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;type&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;link&amp;quot;&lt;/span&gt;,
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;lineno&amp;quot;&lt;/span&gt;: &lt;span class=&quot;number&quot;&gt;38&lt;/span&gt;,
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;colno&amp;quot;&lt;/span&gt;: &lt;span class=&quot;number&quot;&gt;7&lt;/span&gt;,
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;len&amp;quot;&lt;/span&gt;: &lt;span class=&quot;number&quot;&gt;15&lt;/span&gt;,
      &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;to&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;#L6&amp;quot;&lt;/span&gt;
    },
    ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can probably infer from this that annotations are very powerful. Not only can you annotate your code’s semantic elements to your heart’s content, but you can also do exotic things we haven’t thought of yet, for every programming language you can find a parser for.&lt;/p&gt;&lt;p&gt;I’ll be going into some detail on the thought process that went into this feature’s design and implementation in a moment, but if you’re just excited and want to try it out, here are a few interesting annotated repos to browse:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc/tree/master/src/main.c&quot; target=&quot;_blank&quot;&gt;~sircmpwn/scdoc&lt;/a&gt;: man page generator (C)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc/tree/master/widgets/msgviewer.go&quot; target=&quot;_blank&quot;&gt;~sircmpwn/aerc&lt;/a&gt;: TUI email client (Go)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~mcf/cproc/tree/master/scan.c&quot; target=&quot;_blank&quot;&gt;~mcf/cproc&lt;/a&gt;: C compiler (C)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;And here are the docs for generating your own: &lt;a href=&quot;https://man.sr.ht/git.sr.ht/annotations.md&quot; target=&quot;_blank&quot;&gt;annotations on git.sr.ht&lt;/a&gt;. Currently annotators are available for C and Go, and I intend to write another for Python. For the rest, I’ll be relying on the community to put together annotators for their favorite programming languages, and to help me expand on the ones I’ve built.&lt;/p&gt;&lt;h2&gt;Design&lt;/h2&gt;&lt;p&gt;A lot of design thought went into this feature, but I knew one thing from the outset: I wanted to make a generic system that users could use to annotate their source code in any manner they chose. My friend Andrew Kelley (of &lt;a href=&quot;https://ziglang.org/&quot; target=&quot;_blank&quot;&gt;Zig&lt;/a&gt; fame) once expressed to me his frustration with GitHub’s refusal to implement syntax highlighting for “small” languages, citing a shortage of manpower. It’s for this reason that it’s important to me that SourceHut’s open-source platform allows users large and small to volunteer to build the perfect integration for their needs - I don’t scale alone&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;To get a head start for the most common use-cases - scanning source files and linking references and definitions together - the best approach was unclear. I spent a lot of time studying &lt;a href=&quot;http://ctags.sourceforge.net/&quot; target=&quot;_blank&quot;&gt;ctags&lt;/a&gt;, for example, which supports a huge set of programming languages, but unfortunately only finds definitions. I thought about combining this with another approach for finding references, but the only generic library with lots of parsers I’m aware of is &lt;a href=&quot;http://pygments.org/&quot; target=&quot;_blank&quot;&gt;Pygments&lt;/a&gt;, and I didn’t necessarily want to bring Python into every user’s CI process if they weren’t already using it. That approach would also make it more difficult to customize the annotations for each language. Other options I considered were &lt;a href=&quot;http://cscope.sourceforge.net/&quot; target=&quot;_blank&quot;&gt;cscope&lt;/a&gt; and &lt;a href=&quot;https://www.gnu.org/software/global/&quot; target=&quot;_blank&quot;&gt;gtags&lt;/a&gt;, but the former doesn’t have many programming languages supported (making the tradeoff questionable), and the latter just uses Pygments anyway.&lt;/p&gt;&lt;p&gt;So I decided: I’m going to write my own annotators for each language. Or at least the languages I use the most:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;C, because I like it but also because &lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc&quot; target=&quot;_blank&quot;&gt;scdoc&lt;/a&gt; is the demo repo shown on the &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut marketing page&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Python, because SourceHut is largely written in Python and using it to browse itself would be cool.&lt;/li&gt;&lt;li&gt;Go, because parts of SourceHut are written in it but also because I use it a lot for &lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc&quot; target=&quot;_blank&quot;&gt;my own projects&lt;/a&gt;. I also knew that Go had at least &lt;em&gt;some&lt;/em&gt; first-class support for working with its AST - and boy was I in for a surprise.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With these initial languages decided, let’s turn to the implementations.&lt;/p&gt;&lt;h2&gt;Annotating C code&lt;/h2&gt;&lt;p&gt;I began with the C annotator, because I knew it would be the most difficult. There does not exist any widely available standalone C parsing library to provide C programs with access to an AST. There’s LLVM, but I have a deeply held belief that programming language compiler and introspection tooling should be implemented in the language itself. So, I set about to write a C parser from scratch.&lt;/p&gt;&lt;p&gt;Or, almost from scratch. There exist two standard POSIX tools for writing compilers with: &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/lex.html&quot; target=&quot;_blank&quot;&gt;lex&lt;/a&gt; and &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/9699919799/utilities/yacc.html&quot; target=&quot;_blank&quot;&gt;yacc&lt;/a&gt;, which are respectively a lexer generator and a compiler compiler. Additionally, there are &lt;a href=&quot;http://www.quut.com/c/ANSI-C-grammar-y.html&quot; target=&quot;_blank&quot;&gt;pre-fab lex and yacc files&lt;/a&gt; which &lt;em&gt;mostly&lt;/em&gt; implement the C11 standard grammar. However, C is &lt;a href=&quot;https://eli.thegreenplace.net/2007/11/24/the-context-sensitivity-of-cs-grammar/&quot; target=&quot;_blank&quot;&gt;not a context-free language&lt;/a&gt;, so additional work was necessary to track typedefs and use them to change future tokens emitted by the scanner. A little more work was also necessary for keeping track of line and column numbers in the lexer. Overall, however, this was relatively easy, and in less than a day’s work I had a fully functional C11 parser.&lt;/p&gt;&lt;p&gt;However, my celebration was short-lived as I started to feed my parser C programs from the wild. The GNU C Compiler, GCC, implements many C extensions, and their use, while inadvisable, is extremely common. Not least of the offenders is glibc, and thus running my parser on any system with glibc headers installed would likely immediately run into syntax errors.  GCC’s extensions are not documented in the form of an addendum to the C specification, but rather as end-user documentation and a 15 million lines-of-code compiler for you to reverse engineer. It took me almost a week of frustration to get a parser which worked passably on a large subset of the C programs found in the wild, and I imagine I’ll be dealing with GNU problems for years to come. Please don’t use C extensions, folks.&lt;/p&gt;&lt;p&gt;In any case, the result now works fairly well for a lot of programs, and I have plans on expanding it to integrate more nicely with build systems like meson. Check out the code here: &lt;a href=&quot;https://git.sr.ht/~sircmpwn/annotatec&quot; target=&quot;_blank&quot;&gt;annotatec&lt;/a&gt;. The features of the C annotator include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Annotating function definitions with a list of files/linenos which call them&lt;/li&gt;&lt;li&gt;Linking function calls to the definition of that function&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the future I intend to add support for linking to external symbols as well - for example, linking to the POSIX spec for functions specified by POSIX, or to the Linux man pages for Linux calls. It would also be pretty cool to support linking between related projects, so that wlroots calls in sway can be linked to their declarations in the wlroots repo.&lt;/p&gt;&lt;h2&gt;Annotating Go code&lt;/h2&gt;&lt;p&gt;The Go annotator was far easier. I started over my morning cup of coffee today and I was finished with the basics by lunch. Go has a bunch of support in the standard library for parsing and analyzing Go programs - I was very impressed:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://golang.org/pkg/go/ast/&quot; target=&quot;_blank&quot;&gt;go/ast&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://golang.org/pkg/go/scanner/&quot; target=&quot;_blank&quot;&gt;go/scanner&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://golang.org/pkg/go/token/&quot; target=&quot;_blank&quot;&gt;go/token&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://golang.org/pkg/go/types/&quot; target=&quot;_blank&quot;&gt;go/types&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To support Go 1.12’s go modules, the experimental (but good enough) &lt;a href=&quot;https://godoc.org/golang.org/x/tools/go/packages&quot; target=&quot;_blank&quot;&gt;packages&lt;/a&gt; module is available as well. All of this is nicely summarized by a lovely document in the &lt;a href=&quot;https://github.com/golang/example/tree/master/gotypes&quot; target=&quot;_blank&quot;&gt;golang examples repository&lt;/a&gt;. The type checker is also available as a library, something which is less common even among languages with parsers-as-libraries, and allows for many features which would be very difficult without it. Nice work, Go!&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://git.sr.ht/~sircmpwn/annotatego&quot; target=&quot;_blank&quot;&gt;resulting annotator&lt;/a&gt; clocks in at just over 250 lines of code - compare that to the C annotator’s ~1,300 lines of C, lex, and yacc source code. The Go annotator is more featureful, too, it can:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Link function calls to their definitions, and in reverse&lt;/li&gt;&lt;li&gt;Link method calls to their definitions, and in reverse&lt;/li&gt;&lt;li&gt;Link variables to their definitions, even in other files&lt;/li&gt;&lt;li&gt;Link to godoc for symbols defined in external packages&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I expect a lot more to be possible in the future. It might get noisy if you turn everything on, so each annotation type is gated behind a command line flag.&lt;/p&gt;&lt;h2&gt;Displaying annotations&lt;/h2&gt;&lt;p&gt;Displaying these annotations required a bit more effort than I would have liked, but the end result is fairly clean and reusable. Since SourceHut uses Pygments for syntax highlighting, I ended up writing a &lt;a href=&quot;http://pygments.org/docs/formatterdevelopment/&quot; target=&quot;_blank&quot;&gt;custom Formatter&lt;/a&gt; based on the existing Pygments HtmlFormatter. The result is the &lt;a href=&quot;https://git.sr.ht/~sircmpwn/git.sr.ht/tree/master/gitsrht/annotations.py&quot; target=&quot;_blank&quot;&gt;AnnotationFormatter&lt;/a&gt;, which splices annotations into the highlighted code. One downside of this approach is that it works at the token level - a more sophisticated implementation will be necessary for annotations that span more than a single token. Annotations are fairly expensive to render, so the rendered HTML is stowed in Redis.&lt;/p&gt;&lt;h2&gt;The future?&lt;/h2&gt;&lt;p&gt;I intend to write a Python annotator soon, and I’ll be relying on the community to build more. If you’re looking for a fun weekend hack and a chance to learn more about your favorite programming language, this’d be a great project. The format for annotations on SourceHut is also pretty generalizable, so I encourage other code forges to reuse it so that our annotators are useful on every code hosting platform.&lt;/p&gt;&lt;p&gt;builds.sr.ht will also soon grow first-class support for making these annotators available to your build process, as well as for making an OAuth token available (ideally with a limited set of permissions) to your build environment. Rigging up an annotator is a bit involved today (&lt;a href=&quot;https://man.sr.ht/git.sr.ht/annotations.md&quot; target=&quot;_blank&quot;&gt;though the docs help&lt;/a&gt;), and streamlining that process will be pretty helpful. Additionally, this feature is only available for git.sr.ht, though it should generalize to hg.sr.ht fairly easily and I hope we’ll see it available there soon.&lt;/p&gt;&lt;p&gt;I’m also looking forward to seeing more novel use-cases for annotation. Can we indicate code coverage by coloring a gutter alongside each line of code? Can we link references to ticket numbers in the comments to your bug tracker? If you have any cool ideas, I’m all ears. Here’s that list of cool annotated repos to browse again, if you made it this far and want to check them out:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc/tree/master/src/main.c&quot; target=&quot;_blank&quot;&gt;~sircmpwn/scdoc&lt;/a&gt;: man page generator (C)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc/tree/master/widgets/msgviewer.go&quot; target=&quot;_blank&quot;&gt;~sircmpwn/aerc&lt;/a&gt;: TUI email client (Go)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~mcf/cproc/tree/master/scan.c&quot; target=&quot;_blank&quot;&gt;~mcf/cproc&lt;/a&gt;: C compiler (C)&lt;/li&gt;&lt;/ul&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Announcing-annotations-for-sourcehut/</link>
        
        <pubDate>Mon, 08 Jul 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Announcing-annotations-for-sourcehut/</guid>
      </item>
    
      <item>
        
        
          <title>Absence of certain features in IRC considered a feature</title>
          <description>
            &lt;p&gt;The other day a friend of mine (an oper on Freenode) wanted to talk about IRC compared to its peers, such as Matrix, Slack, Discord, etc. The ensuing discussion deserves summarization here. In short: I’m glad that IRC doesn’t have the features that are “showstoppers” for people choosing other platforms, and I’m worried that attempts to bring these showstopping “features” to IRC will worsen the platform for the people who use it now.&lt;/p&gt;&lt;p&gt;On IRC, features like embedded images, a nice UX for messages longer than a few lines (e.g. pasted code), threaded messages, etc; are absent. Some sort of “graceful degradation” to support mixed channels with clients which support these features and clients which don’t may be possible, but it still &lt;em&gt;degrades&lt;/em&gt; the experience for many people. By instead making everyone work within the limitations of IRC, we establish a shared baseline, and expressing yourself within these limitations is not only possible but makes a better experience for everyone.&lt;/p&gt;&lt;p&gt;Remember that [not everyone is like you][old hardware]. I regularly chat with people on ancient hardware that slows to a crawl when a web browser is running&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, or people working from a niche operating system for which porting a graphical client is a herculean task, or people with accessibility concerns for whom the “one line of text per statement” fits nicely into their TTS&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; system and screenreading Slack is a nightmare.&lt;/p&gt;&lt;p&gt;Let’s consider what happens when these features are added but non-uniformly available. Let’s use rich text as an example and examine the fallback implementation. Which of these is better?&lt;/p&gt;&lt;div&gt;(A) &amp;lt;user&amp;gt; check out [this website](&lt;a href=&quot;https://example.org&quot;&gt;https://example.org&lt;/a&gt;)&lt;/div&gt;
&lt;div&gt;(B) &amp;lt;user&amp;gt; check out this website: &lt;a href=&quot;https://example.org&quot;&gt;https://example.org&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Example B is what people naturally do when rich text is unavailable, and most clients will recognize it as a link and make it clickable anyway. But many clients cannot and will not display example A as a link, which makes it harder to read. Example A also makes phishing &lt;em&gt;much&lt;/em&gt; easier.&lt;/p&gt;&lt;p&gt;Here’s another example: how about a nice UI for long messages, such as pasted code snippets? Let’s examine how three different clients would implement this: (1) a GUI client, (2) a TUI&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; client, and (3) a client which refuses to implement it or is unmaintained&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;The first case is the happy path, we probably get a little scrollbox that the user can interact with their mouse. Let’s say &lt;a href=&quot;https://weechat.org/&quot; target=&quot;_blank&quot;&gt;Weechat&lt;/a&gt; takes up option 2, but how do they do that? Some terminal emulators have mouse support, so they could have a similar box, but since Weechat is primarily keyboard-driven (and some terminal emulators do not support mice!), a keyboard-based alternative will be necessary. Now we have to have some kind of command or keybinding for scrolling through the message, and picking which of the last few long messages we want to scroll through. This will have to be separate from scrolling through the backlog normally, of course. The third option is the worst: they just see a hundred lines pasted into their backlog, which is already highly scorned behavior on most IRC channels. Only the GUI users come away from this happy, and on IRC they’re in the minority.&lt;/p&gt;&lt;p&gt;Some IRC clients (Matrix) have this feature today, but most Matrix users don’t realize what a nuisance they’re being on the chat. Here’s what they see:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/VOeY.png&quot;&gt;&lt;/p&gt;&lt;p&gt;And here’s what I see:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/HZ7Z.png&quot;&gt;&lt;/p&gt;&lt;p&gt;Conservative improvements built on top of existing IRC norms, such as &lt;a href=&quot;https://thelounge.chat/&quot; target=&quot;_blank&quot;&gt;The Lounge&lt;/a&gt;, are much better. Most people post images on IRC as URLs, which clients can do a quick HEAD request against and embed if the mimetype is appropriate:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/9RsR.png&quot;&gt;&lt;/p&gt;&lt;p&gt;For most of these features, I think that people who have and think they need them are in fact unhappier for having them. What are some of the most common complaints from Slack users et al? “It’s distracting.” “It’s hard to keep up with what people said while I was away.” “Threads get too long and hard to understand.” Does any of this sound familiar? Most of these problems are caused by or exacerbated by features which are missing from IRC.  It’s distracting because your colleagues are posting gifs all day. It’s hard to keep up with because the infinite backlog encourages a culture of catching up rather than setting the expectation that conversations are ephemeral&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. Long conversations shouldn’t be organized into threads, but moved into email or another medium more suitable for that purpose.&lt;/p&gt;&lt;p&gt;None of this even considers what &lt;em&gt;is&lt;/em&gt; good about IRC. It’s a series of decentralized networks built on the shoulders of volunteers. It’s venerable and well-supported with hundreds of client and server implementations. You can connect to IRC manually using telnet and have a pretty good user experience! Accordingly, &lt;a href=&quot;https://drewdevault.com/2018/03/10/How-to-write-an-IRC-bot.html&quot; target=&quot;_blank&quot;&gt;a working IRC bot can be written in about 2 minutes&lt;/a&gt;. No one is trying to monetize you on IRC. It’s free, in both meanings, and nothing which has come since has presented a compelling alternative. I’ve used IRC all day, every day for over ten years, and that’s not even half of IRC’s lifetime. It’s outlived everything else by years and years, and it’s not going anywhere soon.&lt;/p&gt;&lt;p&gt;In summary, I like IRC the way it is. It has problems which we ought to address, but many people focus on the wrong problems. The culture that it fosters is good and worth preserving, even at the expense of the features users of other platforms demand - or those users themselves.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;P.S. A friend pointed out that the migration of non-hackers away from IRC is like a reverse &lt;a href=&quot;https://en.wikipedia.org/wiki/Eternal_September&quot; target=&quot;_blank&quot;&gt;Eternal September&lt;/a&gt;, which sounds &lt;em&gt;great&lt;/em&gt; 😉&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Absence-of-features-in-IRC/</link>
        
        <pubDate>Mon, 01 Jul 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Absence-of-features-in-IRC/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, June 2019</title>
          <description>
            &lt;p&gt;Summer is in full swing here in Philadelphia. Last night I got great views of Jupiter and a nearly-full Moon, and my first Saturn observation of the year.  I love astronomy on clear Friday nights, there’s always plenty of people coming through the city. And today, on a relaxing lazy Saturday, waiting for friends for dinner later, I have the privilege of sharing another status report with you.&lt;/p&gt;&lt;p&gt;First, I want to talk about some work I’ve done with blogs lately. On the bottom of this article you’ll find a few blog posts from around the net. This is populated with &lt;a href=&quot;https://git.sr.ht/~sircmpwn/openring&quot; target=&quot;_blank&quot;&gt;openring&lt;/a&gt;, a small Go tool I made to fetch a few articles from a list of RSS feeds. A couple of other people have added this to their own sites as well, and I hope to use this to encourage the growth of a network of bloggers supporting each other without any nonfree or centralized software. I’ll write about this in its own article in time. I’ve also made an &lt;a href=&quot;https://drewdevault.com/make-a-blog/&quot;&gt;open offer&lt;/a&gt; to give $20 to anyone who wants to make their own blog, and so far 5 new blogs have taken me up on the offer. Maybe you’ll be the next?&lt;/p&gt;&lt;p&gt;Other side projects have seen some nice progress this month, too. &lt;a href=&quot;https://git.sr.ht/~sircmpwn/wio&quot; target=&quot;_blank&quot;&gt;Wio&lt;/a&gt; has received a few patches from Leon Plickat improving the UX, and I understand more are on the way. I’m also happy to tell you that the RISC-V musl libc port I was working on is heading upstream and slated for inclusion in the next release! Big thanks to everyone who helped with that, and to Rich Felker for reviewing it and assembling the final patches. I was also able to find some time this month to contribute to &lt;a href=&quot;https://git.sr.ht/~emersion/mrsh&quot; target=&quot;_blank&quot;&gt;mrsh&lt;/a&gt;, adding support for job IDs, the &lt;code&gt;wait&lt;/code&gt;, &lt;code&gt;break&lt;/code&gt;, and &lt;code&gt;continue&lt;/code&gt; builtins, and a handful of other improvements. I’m really excited about mrsh, it’s getting close to completion. My friend Luminarys also finally released &lt;a href=&quot;https://synapse-bt.org/&quot; target=&quot;_blank&quot;&gt;synapse 1.0&lt;/a&gt;, a bittorrent client that I had a &lt;a href=&quot;https://github.com/Luminarys/synapse/commit/ac92bb424c3d7d99905f4c0988c924001b688080#diff-d981183863e690e9f0f2bd20145a7a16&quot; target=&quot;_blank&quot;&gt;hand in designing&lt;/a&gt;, and &lt;a href=&quot;https://github.com/ddevault/receptor&quot; target=&quot;_blank&quot;&gt;building&lt;/a&gt; &lt;a href=&quot;https://broca.synapse-bt.org/&quot; target=&quot;_blank&quot;&gt;frontends&lt;/a&gt; for. Congrats, Lumi! This one has been a long time coming.&lt;/p&gt;&lt;p&gt;Alright, now for some updates on the larger, long-term projects. The initial pre-release of aerc &lt;a href=&quot;https://drewdevault.com/blog/Announcing-aerc-0.1.0/&quot;&gt;shipped&lt;/a&gt; two weeks ago! Even since then it’s already attracted a flurry of patches from the community. I’m tremendously excited about this project, I think it has heaps of potential and a community is quickly forming to help us live up to it. Since 0.1.0 it’s already grown support for formatting the index list, swapped the Python dependency for POSIX awk, grown temporary accounts and the ability to view headers, and more. I’ve already started planning 0.2.0 - check out &lt;a href=&quot;https://todo.sr.ht/~sircmpwn/aerc2?search=label:%22blocker%22%20status%3Aopen&quot; target=&quot;_blank&quot;&gt;the list of blockers&lt;/a&gt; for a sneak peek.&lt;/p&gt;&lt;p&gt;The Godot+Wayland workstream has picked up again, and I’ve secured some VR hardware (an HTC Vive) and started working on &lt;a href=&quot;https://github.com/swaywm/wlroots/issues/1723&quot; target=&quot;_blank&quot;&gt;planning the changes necessary&lt;/a&gt; for first-class VR support on wlroots. In the future I also would like to contribute with the OpenXR and OpenHMD efforts for bringing a full-stack free software solution for VR. I also did a proof-of-concept 3D Wayland compositor that I intend to translate to VR once I have the system up and running on Wayland:&lt;/p&gt;&lt;p&gt;&lt;video autoplay muted controls&gt;
&lt;source src=&quot;https://yukari.sr.ht/godot3d.webm&quot;&gt;
&lt;/video&gt;&lt;/p&gt;&lt;p&gt;In other respects, sway &amp; wlroots have been somewhat quiet. We’ve been focusing on small bug fixes and quality-of-life improvements, while some beefier changes are stewing on the horizon. wlroots has seen some slow and steady progress on refining its DRM implementation, improvements to which are going to lead to even further improved performance and capability of the downstream compositors - notably, direct scan-out has just been merged with the help of Scott Anderson and Simon Ser.&lt;/p&gt;&lt;p&gt;In SourceHut news, the most exciting is perhaps that todo.sr.ht has grown an API and webhooks! That makes it the last major sr.ht service to gain these features, which unblocks a lot of other stuff in the pipeline. The biggest workstream unblocked by this is dispatch.sr.ht, which has an design proposal for an overhaul under discussion on the development list. This’ll open the door for features like building patches sent to mailing lists, linking tickets to commits, and much more. I’ve also deployed another compute server to pick up the load as git.sr.ht grows to demand more resources, which frees up the box it used to be on with more space for smaller services to get comfortable. I was also happy to bring Ludovic Chabant, the driving force behind hg.sr.ht, with me to attend a Mercurial conference in Paris, where I learned heaps about the internals (and externals, to be honest) of Mercurial. Cool things are in store here, too! Big thanks to the Mercurial maintainers for being so accommodating of my ignorance, and for putting on a friendly and productive conference.&lt;/p&gt;&lt;p&gt;In the next month, I’m moving aerc to the backburner and turning my focus back to SourceHut &amp; wlroots VR. I’m getting a consistent stream of great patches for aerc to review, so I’m happy to leave it in the community’s hands for a while. For SourceHut, the upcoming dispatch workstream is going to be a huge boon to the community there. On its coattails will come more powerful data import &amp; export tools, giving the users more ownership and autonomy over their data, and perhaps following this will be some nice improvements to git.sr.ht. I’m also going to try and find time to invest more in Alpine Linux on RISC-V this month.&lt;/p&gt;&lt;p&gt;From the bottom of my heart, thank you again for lending your support. I’ve never been busier, happier, and more productive than I have been since working on FOSS full-time. Let’s keep building awesome software together.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-June-2019/</link>
        
        <pubDate>Sat, 15 Jun 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-June-2019/</guid>
      </item>
    
      <item>
        
        
          <title>My personal journey from MIT to GPL</title>
          <description>
            &lt;p&gt;As I got started writing open source software, I generally preferred the MIT license. I actually made fun of the “copyleft” GPL licenses, on the grounds that they are less free. I still hold this opinion today: the GPL license is less free than the MIT license - but today, I believe this in a good way.&lt;/p&gt;&lt;p&gt;If you haven’t yet, I suggest reading the &lt;a href=&quot;https://opensource.org/licenses/MIT&quot; target=&quot;_blank&quot;&gt;MIT license&lt;/a&gt; - it’s very short. It satisfies the four essential freedoms guaranteed of &lt;a href=&quot;https://www.gnu.org/philosophy/free-sw.html&quot; target=&quot;_blank&quot;&gt;free software&lt;/a&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The right to use the software for any purpose.&lt;/li&gt;&lt;li&gt;The right to study the source code and change it as you please.&lt;/li&gt;&lt;li&gt;The right to redistribute the software to others.&lt;/li&gt;&lt;li&gt;The right to distribute your modifications to the software.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The MIT license basically allows you to do whatever you want with the software. It’s one of the most hands-off options: “here’s some code, you can do anything you want with it.” I favored this because I wanted to give users as much freedom to use my software as possible. The GPL, in addition to being a &lt;a href=&quot;https://www.gnu.org/licenses/gpl-3.0.html&quot; target=&quot;_blank&quot;&gt;much more complex tome to understand&lt;/a&gt;, is more restrictive. The GPL forces you to use the GPL for derivative works as well. Clearly this affords you less freedom to use the software. Obligations are the opposite of freedoms.&lt;/p&gt;&lt;p&gt;When I first got into open source, I was still a Windows user. As I gradually waded deeper and deeper into the free software pond, I began to use Linux more often&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Even once I started using Linux as my daily driver, however, it took a while still for the importance of free software to set in. But this realization is inevitable, for a programmer immersed in Linux. It radically changes your perspective when all of the software you use guarantees these four freedoms. If I’m curious about how something works, I can usually be reading the code within a few seconds. I can find the author’s name and email in the git blame and shoot them some questions. And when I find a bug, I can fix it and send them a patch.&lt;/p&gt;&lt;p&gt;The weight of these possibilities did not occur to me immediately, instead slowly becoming evident over time. Today, this cycle is almost muscle memory. Pulling down source, grepping for files related to an itch I need to scratch, compiling and installing the modified version, and sending my work upstream - it’s become second nature to me. These days, on the rare occasion that I run into some proprietary software, this all grinds to a halt. It’s like miscounting the number of steps on your staircase in the dark. These moments drive the truth home: Free software is good. It’s starkly better than the alternative. And copyleft defends it. Now that I’ve had a taste, you bet your ass I’m not going to give it up.&lt;/p&gt;&lt;p&gt;As the number of hours I’ve spent on FOSS projects grew from tens of hours, to hundreds, to thousands and tens of thousands, I’ve learned that the effort I sink into my work far outstrips the effort required to reuse my work. The collective effort of the free software community amounts to tens of millions of hours of work, which you can download at touch of a button, for free. If the people with their fingers on that button held these same ideals, we wouldn’t need the GPL. The reality, however, is that we live in a capitalist world. Our socialist free software utopia is ripe for exploitation by capitalists, and they’ll be rewarded for doing so. Capitalism is about enriching yourself - not enriching your users and certainly not enriching society.&lt;/p&gt;&lt;p&gt;Your parents probably taught you about the Golden Rule when you were young: do unto others as you would have them do unto you. The GPL is the legal embodiment of this Golden Rule: in exchange for benefiting from my hard work, you just have to extend me the same courtesy. Its the unfortunate acknowledgement that we’ve created a society that incentivises people to forget the Golden Rule. I give people free software because I want them to reciprocate with the same. That’s really all the GPL does. Its restrictions just protect the four freedoms in derivative works. Anyone who can’t agree to this is looking to exploit your work for their gain - and definitely not yours.&lt;/p&gt;&lt;p&gt;I don’t plan on relicensing my historical projects, but my new projects have used the GPL family of licenses for a while now. I think you should seriously consider it as well.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/My-journey-from-MIT-to-GPL/</link>
        
        <pubDate>Thu, 13 Jun 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/My-journey-from-MIT-to-GPL/</guid>
      </item>
    
      <item>
        
        
          <title>Initial pre-release of aerc: an email client for your terminal</title>
          <description>
            &lt;p&gt;After years of painfully slow development, the &lt;a href=&quot;https://aerc-mail.org&quot; target=&quot;_blank&quot;&gt;aerc email client&lt;/a&gt; has seen a huge boost in its pace of development recently. This leads to today’s announcement: &lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc/refs/0.1.0&quot; target=&quot;_blank&quot;&gt;aerc 0.1.0 is now available&lt;/a&gt;! After my transition to &lt;a href=&quot;https://drewdevault.com/2019/01/15/Im-doing-FOSS-full-time.html&quot; target=&quot;_blank&quot;&gt;working on free software full time&lt;/a&gt; allowed me to spend more time on more projects, I was able to invest considerably more time into aerc. Your support led us here: thank you to all of the people who &lt;a href=&quot;https://drewdevault.com/donate&quot; target=&quot;_blank&quot;&gt;donate to my work&lt;/a&gt;!&lt;/p&gt;&lt;p&gt;I’ve prepared a short webcast demonstrating aerc’s basic features - give it a watch if you’re curious about what aerc looks like &amp; what makes it interesting.&lt;/p&gt;&lt;p&gt;&lt;video controls&gt;
&lt;source src=&quot;https://aerc-mail.org/aerc-intro.webm&quot;&gt;
&lt;/video&gt;&lt;/p&gt;&lt;p&gt;In summary, aerc is an email client which runs in your terminal emulator. If you’re coming from mutt, you’ll appreciate its more efficient &amp; reliable networking, a keybinding system closer to vims, and embedded terminal emulator allowing you to compose emails and read new ones at the same time. It builds on this foundation with a lot of new and exciting features. For example, its “filter” feature allows us to review patches with syntax highlighting:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/JoqH.png&quot;&gt;
&lt;figcaption&gt;Screenshot of aerc displaying a patch&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;The embedded terminal emulator also allows us convenient access to nearby git repositories for running tests against incoming patches, pushing the changes once accepted, or anything else you might want to do. Want to run &lt;a href=&quot;https://weechat.org/&quot; target=&quot;_blank&quot;&gt;Weechat&lt;/a&gt; in an aerc tab? Just like that, aerc has a chat client! Writing emails in vim, manipulating git &amp; hg repositories, playing nethack to kill some time… all stuff you never realized your email client was missing.&lt;/p&gt;&lt;p&gt;I plan on extending aerc in the future with more integrations with version control systems, calendar &amp; contacts support, and more email configurations like notmuch and JMAP. Please consider &lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc&quot; target=&quot;_blank&quot;&gt;contributing&lt;/a&gt; if you’re interested in writing a little Go, or &lt;a href=&quot;https://drewdevault.com/donate&quot; target=&quot;_blank&quot;&gt;donating monthly&lt;/a&gt; to ensure I always have time to work on this and other free software projects. Give aerc a try and let me know what you think!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Announcing-aerc-0.1.0/</link>
        
        <pubDate>Mon, 03 Jun 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Announcing-aerc-0.1.0/</guid>
      </item>
    
      <item>
        
        
          <title>What is a fork, really, and how GitHub changed its meaning</title>
          <description>
            &lt;p&gt;The fork button on GitHub - with the little number next to it for depositing dopamine into your brain - is a bit misleading. GitHub co-opted the meaning of “fork” to trick you into participating in their platform more. They did this in a well-intentioned way, for the sake of their pull requests feature, but ultimately this design is self-serving and causes some friction when contributors venture out of their GitHub sandbox and into the rest of the software development ecosystem. Let’s clarify what “fork” really means, and what we do without GitHub’s concept of one - for it is in this difference that we truly discover how git is a &lt;em&gt;distributed&lt;/em&gt; version control system.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: I am the founder of &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut&lt;/a&gt;, a product which competes with GitHub and embraces the “bazaar&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;” model described in this article.&lt;/p&gt;&lt;p&gt;On GitHub, a fork refers to a copy of a repository used by a contributor&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; to stage changes they’d like to propose upstream. Prior to GitHub (and in many places still today), we’d call such a repository a “personal branch”. A personal branch doesn’t need to be published to be useful - you can just &lt;code&gt;git clone&lt;/code&gt; it locally and make your changes there without pushing them to a public, hosted repository. Using &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;email&lt;/a&gt;, you can send changes from your local, unpublished repository for consideration upstream. Outside of GitHub and its imitators, most contributors to a project don’t have a published version of their repository online at all, skipping that step and saving some time.&lt;/p&gt;&lt;p&gt;In some cases, however, it’s useful to publish your personal branch online. This is often done when a team of people is working on a long-lived branch to later propose upstream - for example, I’ve been doing this while working on the RISC-V port of musl libc. It gives us a space to collaborate and work while preparing changes which will eventually be proposed upstream, as well as a place for interested testers to obtain our experimental work to try themselves. This is also done by individuals, such as Greg Kroah-Hartman’s Linux branches, which are useful for testing upcoming changes to the Linux kernel.&lt;/p&gt;&lt;p&gt;Greg is not alone in publishing a repo like this. In fact, there are &lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/&quot; target=&quot;_blank&quot;&gt;hundreds of kernel trees like this&lt;/a&gt;. These act as staging areas for long-term workstreams, or for the maintainers of many subsystems of the kernel.  Changes in these repositories gradually flow upwards towards the “main” tree, &lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/&quot; target=&quot;_blank&quot;&gt;torvalds/linux&lt;/a&gt;. The precise meaning of “linux” is rather loose in this context. An argument could be made that torvalds/linux is Linux, but that definition wouldn’t capture the LTS branches. Many distros also apply their own patches on top of Torvalds, perhaps sourcing them from the maintainers of drivers they need a bugfix for, or they maintain their own independent trees which periodically pull in lump sums of changes from other trees - meaning that the simple definition might not include the version of Linux which is installed on your computer, either. This ambiguity is a feature - each of these trees is a valid definition of Linux in its own right.&lt;/p&gt;&lt;p&gt;This is the sense in which git is “distributed”. The idea of a canonical upstream is not written in stone in the way that GitHub suggests it might be. After all, open-source software is a collaborative endeavour. What makes Jim’s branch more important that John’s branch? John’s branch is definitely more important if it has the bugfixes you need. In fact, your branch, based on Jim’s, with some patches cherry-picked from John, and a couple of fixes of your own mixed in, may in fact be the best version of the software for you.&lt;/p&gt;&lt;p&gt;This is how the git community gets along without the GitHub model of “forks”. This design has allowed the largest and most important projects in the world to flourish, and git was explicitly designed around this model. We refer to this as the “bazaar” model, the metaphor hopefully being fairly obvious at this point. There is another model, which GitHub embodies instead: the cathedral. In this model, the project has a central home and centralized governance, run by a small number of people. The cathedral doesn’t necessarily depend on the GitHub idea of “forks” and pull requests - that is, you can construct a cathedral with email-driven development or some other model - but on GitHub the bazaar option is basically absent.&lt;/p&gt;&lt;p&gt;In the introduction I said that GitHub attempts to replace an existing meaning for “fork”. So what does forking actually mean, then? Consider a project with the cathedral model. What happens when there’s a schism in the church? The answer is that some of the contributors can take the code, put up a new branch somewhere, and stake a flag in the ground. They rename it and commit to maintaining it entirely independently of the original project, and encourage contributors, new and old alike, to abandon the old dogma in favor of theirs. At this point, the history&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; begins to diverge. The new contingent pulls in all of the patches that were denied upstream and start that big refactoring to mold it in their vision. The project has been &lt;strong&gt;forked&lt;/strong&gt;. A well known example is when ffmpeg was forked to create libav.&lt;/p&gt;&lt;p&gt;This is usually a traumatic event for the project, and can have repercussions that last for years. The precise considerations that should go into forking a project, these repercussions and how to address them, and other musings are better suited for a separate article. But this is what “fork” meant before GitHub, and this meaning is still used today - albeit more ambiguously.&lt;/p&gt;&lt;p&gt;If “fork” already had this meaning, why did GitHub adopt their model? The answer, as it often will be, is centralization of power. GitHub is a proprietary, commercial service, and their ultimate goal is to turn a profit. The design of GitHub’s fork and pull request model creates a cathedral that keeps people on their platform in a way that a bazaar would not. A distributed version control system like git, built on a distributed communications protocol like email, is hard to disrupt with a centralized service. So GitHub designed their own model.&lt;/p&gt;&lt;p&gt;As a parting note, I would like to clarify that this isn’t a condemnation of GitHub. I still use their service for a few projects, and appreciate the important role GitHub has played in the popularization of open source. However, I think it’s important to examine the services we depend on, to strive to understand their motivations and design. I also hope the reader will view the software ecosystem through a more interesting lens for having read this article. Thank you for reading!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; Did you know that GitHub also captured the meaning of “pull request” from git’s own &lt;a href=&quot;https://www.git-scm.com/docs/git-request-pull&quot; target=&quot;_blank&quot;&gt;request-pull&lt;/a&gt; tool? git request-pull prepares an email which will ask the recipient to fetch changes from a public repository and integrate them into their own branch. This is used when a patch is insufficient - for example, when Linux subsystem maintainers want to ship a large group of changes to Torvalds for the next kernel release. Again, the original version is distributed and bazaar-like, whereas GitHub’s is centralized and makes you stay on their platform.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/What-is-a-fork/</link>
        
        <pubDate>Fri, 24 May 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/What-is-a-fork/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, May 2019</title>
          <description>
            &lt;p&gt;This month, it seems the most exciting developments again come from the realm of email. I’ve got cool email-related news to share for aerc, lists.sr.ht, and todo.sr.ht, and many cool developments in my other projects to share.&lt;/p&gt;&lt;p&gt;Let’s start with lists.sr.ht: I have broken ground on the web-based patch review tools! I promised these features when I started working on sourcehut, to make the email-based workflow more enticing to those who would rather work on the web. Basically, this gives us a Github or Gerrit-esque review UI for patches which arrive on the mailing list. Thanks to &lt;a href=&quot;https://git.sr.ht/~emersion/python-emailthreads&quot; target=&quot;_blank&quot;&gt;a cool library&lt;/a&gt; Simon Ser wrote for me… almost a year ago… I’m able to take a thread of emails discussing a patch and organically convert them into inline feedback on the web.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/sjtE.png&quot;&gt;&lt;/p&gt;&lt;p&gt;This is generated from organic discussions where the participants don’t have to do anything special to participate - in the discussion this screenshot is generated from, the participants aren’t even aware that this process is taking place. This approach allows users who prefer a web-based workflow to interact with traditional email-based patch review seamlessly. Future improvements will include detecting new revisions of a patch, side-by-side diff and diffs between different versions of a patch, and using the web interface to review a patch - which will generate an email on the list. I’d also like to extend git.sr.ht with web support for git send-email, allowing you to push to your git repo and send a patch off to the mailing list from the web. It should also be possible to combine this with dispatch.sr.ht to have bidirectional code reviews between mailing lists and Github, Gitlab, etc - with no one on either side being any the wiser to the preferred workflow of the other.&lt;/p&gt;&lt;p&gt;In other exciting email-related news, aerc2 now supports composing emails - a feature which has been a long time coming, and was not even present in aerc1! Check it out:&lt;/p&gt;&lt;script
  id=&quot;asciicast-CqTukJZoTq7ZgPmsjhIbQyUjb&quot;
  src=&quot;https://asciinema.org/a/pafXXANiWHY9MOH2yXdVHHJRd.js&quot; async
&gt;&lt;/script&gt;
&lt;p&gt;Outgoing email configuration supports SMTP, STARTTLS, and SMTPS, with sendmail support planned. Outgoing emails are edited with our embedded terminal emulator using vim, or your favorite &lt;code&gt;$EDITOR&lt;/code&gt;. Still to come: replying to emails &amp; PGP support. I could use your help here! If you want a chance to write some cool Go code, stop by the IRC channel and say hello: &lt;a href=&quot;http://webchat.freenode.net/?channels=aerc&amp;uio=d4&quot; target=&quot;_blank&quot;&gt;#aerc on irc.freenode.net&lt;/a&gt;. Once aerc matures a little bit, I also want to start working on a git integration which will continue making email an even more compelling platform for software development.&lt;/p&gt;&lt;p&gt;Let’s talk about Wayland next. I’ve been shipping release candidates for sway 1.1 - &lt;a href=&quot;https://github.com/swaywm/sway/issues/3861#issuecomment-487073065&quot; target=&quot;_blank&quot;&gt;check out the provisional changelog here&lt;/a&gt;. The highlights are probably the ability to inhibit idle with arbitrary criteria, and touch support for swaybar. The release candidates have been pretty quiet - we might end up shipping this as early as rc4. wlroots 0.6.0 was also released, though for end-users it doesn’t include much. We’ve removed the long-deprecated wl_shell, and have made plans to start removing other deprecated protocols. I’ve also been working with the broader Wayland community on establishing a governance model for protocol standardization - &lt;a href=&quot;https://lists.freedesktop.org/archives/wayland-devel/2019-May/040532.html&quot; target=&quot;_blank&quot;&gt;read the latest draft here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I’ve also started working on a Wayland book! It’s intended as a comprehensive reference on the Wayland protocol, useful for authors hoping to write both Wayland compositors and Wayland clients. It does not go into all of the nitty-gritty details necessary for writing a Wayland compositor for Linux (that is, the sort of knowledge necessary for using wlroots, or even making wlroots itself), but that’ll be a task for another time. Instead, I focus on the Wayland protocol itself, explaining how the wire protocol works and the purpose and usage of each interface in &lt;code&gt;wayland.xml&lt;/code&gt;, as well as &lt;code&gt;libwayland&lt;/code&gt;. I intend to sell this book, but when you buy it you’ll receive a DRM-free CC-NC-ND copy that you can share freely with your friends.&lt;/p&gt;&lt;p&gt;Before I move on from Wayland news, also check out &lt;a href=&quot;https://wio-project.org/&quot; target=&quot;_blank&quot;&gt;Wio&lt;/a&gt; if you haven’t yet - I wrote a blog post about it &lt;a href=&quot;https://drewdevault.com/2019/05/01/Announcing-wio.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. In short: I made a novel new Wayland compositor in my spare time which behaves like plan 9’s Rio.  See the blog post for more details!&lt;/p&gt;&lt;p&gt;Following the success of &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;git-send-email.io&lt;/a&gt;, I published a similar website last week: &lt;a href=&quot;https://git-rebase.io&quot; target=&quot;_blank&quot;&gt;git-rebase.io&lt;/a&gt;. The purpose of this website is to teach readers how to use git rebase, explaining how to use its primitives to accomplish common high-level tasks in a way that leaves the reader equipped to apply those primitives to novel high-level tasks in the course of their work. I hope you find it helpful! I’ve also secured git-filter-branch.io and git-bisect.io to explain additional useful, but confusing git commands in the future.&lt;/p&gt;&lt;p&gt;Brief updates for other projects: I’ve been ramping up RISC-V work again, helping Golang test their port, testing out u-Boot, and working on the Alpine port some more. cozy has seen only a little progress, but the parser is improving and it’s now emitting a (very incomplete) AST for source files you feed to it. Godot is on hold pending additional upstream bandwidth for code review.&lt;/p&gt;&lt;p&gt;That’s all for today! Thank you so much for your support. It’s pretty clear by now that my productivity is way higher now that I’m able to work full-time on open source, thanks to your support. I’ll see you for next month’s update!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-May-2019/</link>
        
        <pubDate>Wed, 15 May 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-May-2019/</guid>
      </item>
    
      <item>
        
        
          <title>Webcast: Reviewing git &amp; mercurial patches with email</title>
          <description>
            &lt;p&gt;With the availability of new resources like &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;git-send-email.io&lt;/a&gt;, I’ve been working on making the email-based workflow more understandable and accessible to the world. One thing that’s notably missing from this tutorial, however, is the maintainer side of the work. I intend to do a full write-up in the future, but for now I thought it’d be helpful to clarify my workflow a bit with a short webcast. In this video, I narrate my workflow as I review a few &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;sourcehut&lt;/a&gt; patches and participate in some dicsussions.&lt;/p&gt;&lt;p&gt;&lt;video controls&gt;
&lt;source src=&quot;https://yukari.sr.ht/git-screencast.webm&quot;&gt;
&lt;/video&gt;&lt;/p&gt;&lt;p&gt;Links:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.mutt.org/&quot; target=&quot;_blank&quot;&gt;mutt&lt;/a&gt;: my email client&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/dotfiles/tree/master/.config/mutt/muttrc&quot; target=&quot;_blank&quot;&gt;my personal mutt config&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/dotfiles/tree/master/bin/semver&quot; target=&quot;_blank&quot;&gt;my “semver” script&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Also check out &lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc2&quot; target=&quot;_blank&quot;&gt;aerc&lt;/a&gt;, a replacement for mutt that I’ve been working on over the past year or two. I will be writing more about that project soon.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Git-email-webcast/</link>
        
        <pubDate>Mon, 13 May 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Git-email-webcast/</guid>
      </item>
    
      <item>
        
        
          <title>Announcing Wio: A clone of Plan 9&apos;s Rio for Wayland</title>
          <description>
            &lt;p&gt;For a few hours here and there over the past few months, I’ve been working on a side project: &lt;a href=&quot;https://wio-project.org&quot; target=&quot;_blank&quot;&gt;Wio&lt;/a&gt;. I’ll just let the (3 minute) screencast do the talking first:&lt;/p&gt;&lt;p&gt;&lt;video src=&quot;https://yukari.sr.ht/wio.webm&quot; controls&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: this video begins with several seconds of grey video. This is normal.&lt;/p&gt;&lt;p&gt;In short, Wio is a Wayland compositor based on wlroots which has a similar look and feel to Plan 9’s Rio desktop. It works by running each application in its own nested Wayland compositor, based on &lt;a href=&quot;https://www.hjdskes.nl/projects/cage/&quot; target=&quot;_blank&quot;&gt;Cage&lt;/a&gt; - yet another wlroots-based Wayland compositor. I used Cage in &lt;a href=&quot;https://drewdevault.com/2019/04/23/Using-cage-for-a-seamless-RDP-Wayland-desktop.html&quot; target=&quot;_blank&quot;&gt;last week’s RDP article&lt;/a&gt;, but here’s another cool use-case for it.&lt;/p&gt;&lt;p&gt;The behavior this allows for (each window taking over its parent’s window, rather than spawning a new window) has been something I wanted to demonstrate on Wayland for a very long time. This is a good demonstration of how Wayland’s fundamentally different and conservative design allows for some interesting use-cases which aren’t possible at all on X11.&lt;/p&gt;&lt;p&gt;I’ve also given Wio some nice features which are easy thanks to wlroots, but difficult on Plan 9 without kernel hacking. Namely, these are multihead support, HiDPI support, and support for the wlroots layer shell protocol. Several other wlroots protocols were invited to the party, useful for taking screenshots, redshift, and so on. Layer shell support is particularly cool, since programs like swaybg and waybar work on Wio.&lt;/p&gt;&lt;p&gt;In terms of Rio compatability, Wio has a ways to go. I would seriously appreciate help from users who are interested in improving Wio. Some notably missing features include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Any kind of filesystem resembling Rio’s window management filesystem. In theory this ought to be do-able with FUSE, at least in part (/dev/text might be tough).&lt;/li&gt;&lt;li&gt;Running every application in its own namespace, for double the Plan 9&lt;/li&gt;&lt;li&gt;Hiding/showing windows (that menu entry is dead)&lt;/li&gt;&lt;li&gt;Joint improvements with Cage to bring greater support for Wayland features, like client-side window resize/move, fullscreen windows, etc&lt;/li&gt;&lt;li&gt;Damage tracking to avoid re-rendering everything on every frame, saving battery life and GPU time&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you’re interested in helping, please join the IRC channel and say hello: &lt;a href=&quot;http://webchat.freenode.net/?channels=%23wio&amp;uio=MTA9dHJ1ZSYxMT0xNzQmMTM9ZmFsc2U4c&quot; target=&quot;_blank&quot;&gt;#wio on irc.freenode.net&lt;/a&gt;. For Wio’s source code and other information, visit the website at &lt;a href=&quot;https://wio-project.org&quot; target=&quot;_blank&quot;&gt;wio-project.org&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Announcing-wio/</link>
        
        <pubDate>Wed, 01 May 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Announcing-wio/</guid>
      </item>
    
      <item>
        
        
          <title>The &quot;shut up and get back to work&quot; coding style guide</title>
          <description>
            &lt;p&gt;So you’re starting a new website, and you open the first CSS file. What style do you use? Well, you hate indenting with spaces passionately. You know tabs are right because they’re literally made for this, and they’re only one byte, and these god damn spaces people with their bloody spacebars…&lt;/p&gt;&lt;p&gt;Shut up and use spaces. That’s how CSS is written&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. And you, mister web programmer, coming out of your shell and dipping your toes into the world of Real Programming, writing your first Golang program: use tabs, jerk. There’s only one principle that matters in coding style: don’t rock the boat. Just do whatever the most common thing is in the language you’re working in. Write your commit messages the same way as everyone else, too. Then shut up and get back to work. This hill isn’t worth dying on.&lt;/p&gt;&lt;p&gt;If you’re working on someone else’s project, this goes double. Don’t get snippy about their coding style. Just follow their style guide, and if there isn’t one, just make your code look like the code around it. It’s none of your goddamn business how they choose to style their code.&lt;/p&gt;&lt;p&gt;Shut up and get back to work.&lt;/p&gt;&lt;p&gt;Ranting aside, seriously - which style guide you use doesn’t matter nearly as much as using one. Just pick the one which is most popular or which is already in use by your peers and roll with it.&lt;/p&gt;&lt;div style=&quot;margin-bottom: 5rem&quot;&gt;&lt;/div&gt;
&lt;p&gt;…though since I’m talking about style anyway, take a look at this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_surface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;wlr_surface_surface_at&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_surface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;surface&lt;/span&gt;,
                                           &lt;span class=&quot;type&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sx&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sy&lt;/span&gt;,
                                           &lt;span class=&quot;type&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sub_x&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sub_y&lt;/span&gt;) {
    &lt;span class=&quot;comment&quot;&gt;// Do stuff&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s a lot of stupid crap which ends up in style guides, but this is by far the worst. Look at all that wasted whitespace! There’s no room to write your parameters on the right, and you end up with 3 lines where you could have two. And you have to mix spaces and tabs! God dammit! This is how you should do it:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_surface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;wlr_surface_surface_at&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_surface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;surface&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sx&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;sy&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sub_x&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;sub_y&lt;/span&gt;) {
    &lt;span class=&quot;comment&quot;&gt;// Do stuff&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the extra indent to distinguish the parameters from the body and the missing garish hellscape of whitespace. If you do this in your codebase, I’m not going to argue with you about it, but I am going to have to talk to my therapist about it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Shut-up-and-get-back-to-work-style/</link>
        
        <pubDate>Mon, 29 Apr 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Shut-up-and-get-back-to-work-style/</guid>
      </item>
    
      <item>
        
        
          <title>Using Cage for a seamless remote Wayland session</title>
          <description>
            &lt;p&gt;Congratulations to Jente Hidskes on &lt;a href=&quot;https://www.hjdskes.nl/blog/cage-01/&quot; target=&quot;_blank&quot;&gt;the first release of Cage&lt;/a&gt;! Cage is a Wayland compositor designed for kiosks - though, as you’ll shortly find out, is useful in many unexpected ways. It launches a single application, in fullscreen, and exits the compositor when that application exits. This lets you basically add a DRM+KMS+libinput session to any Wayland-compatible application (or X application via XWayland) and run it in a tiny wlroots compositor.&lt;/p&gt;&lt;p&gt;I actually was planning on writing something like this at some point (for a project which still hasn’t really come off the ground yet), so I was excited when Jente &lt;a href=&quot;https://www.hjdskes.nl/blog/cage/&quot; target=&quot;_blank&quot;&gt;announced it&lt;/a&gt; in December. With the addition of the &lt;a href=&quot;https://github.com/swaywm/wlroots/pull/1578&quot; target=&quot;_blank&quot;&gt;RDP backend&lt;/a&gt; in wlroots, I thought it would be cool to combine these to make a seamless remote desktop experience. In short, I installed &lt;a href=&quot;http://www.freerdp.com/&quot; target=&quot;_blank&quot;&gt;FreeRDP&lt;/a&gt; and Cage on my laptop, and &lt;a href=&quot;https://swaywm.org&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt; on my desktop. On my desktop, I &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/master/docs/env_vars.md#rdp-backend&quot; target=&quot;_blank&quot;&gt;generated TLS certificates per the wlroots docs&lt;/a&gt; and ran sway like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant property&quot;&gt;WLR_RDP_TLS_CERT_PATH&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;HOME&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;/tls.crt&lt;/span&gt; \
&lt;span class=&quot;constant property&quot;&gt;WLR_RDP_TLS_KEY_PATH&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;HOME&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;/tls.key&lt;/span&gt; \
&lt;span class=&quot;constant property&quot;&gt;WLR_BACKENDS&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;=rdp&lt;/span&gt; \
&lt;span class=&quot;constant function&quot;&gt;sway&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, on my laptop, I can run this script:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; [ &lt;span class=&quot;operator&quot;&gt;$&lt;/span&gt;# -eq 0 ]
&lt;span class=&quot;keyword&quot;&gt;then&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;XDG_RUNTIME_DIR&lt;/span&gt;=/tmp
	&lt;span class=&quot;constant function&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;cage&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;sway-remote&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;launch&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;
	&lt;span class=&quot;constant function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;3&lt;/span&gt;
	&lt;span class=&quot;constant function&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;xfreerdp&lt;/span&gt; \
		&lt;span class=&quot;constant&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;homura&lt;/span&gt; \
		&lt;span class=&quot;constant&quot;&gt;--bpp&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;32&lt;/span&gt; \
		&lt;span class=&quot;constant&quot;&gt;--size&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;1280x800&lt;/span&gt; \
		&lt;span class=&quot;constant&quot;&gt;--rfx&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first branch is taken on the first run, and it starts up cage and asks it to run this script again. The second branch then starts up xfreerdp and connects to my desktop (its hostname is &lt;code&gt;homura&lt;/code&gt;). xfreerdp is then fullscreened and all of my laptop’s input events are directed to it. The result is an experience which is basically identical to running sway directly on my laptop, except it’s actually running on my desktop and using the remote desktop protocol to send everything back and forth.&lt;/p&gt;&lt;p&gt;This isn’t especially practical, but it is a cool hack. It’s definitely not network transparency like some people want, but I wasn’t aiming for that. It’s just a neat thing you can do now that we have an RDP backend for wlroots. And congrats again to Jente - be sure to give Cage a look and see if you can think of any other novel use-cases, too!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Using-cage-for-a-seamless-RDP-Wayland-desktop/</link>
        
        <pubDate>Tue, 23 Apr 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Using-cage-for-a-seamless-RDP-Wayland-desktop/</guid>
      </item>
    
      <item>
        
        
          <title>Choosing a VPN service is a serious decision</title>
          <description>
            &lt;p&gt;There’s a disturbing trend in the past year or so of various VPN companies advertising to the general, non-technical public. It’s great that the general public is starting to become more aware of their privacy online, but I’m not a fan of these companies exploiting public paranoia to peddle their wares. Using a VPN in the first place has potentially grave consequences for your privacy - and can often be worse than not using one in the first place.&lt;/p&gt;&lt;p&gt;It’s true that, generally speaking, when you use a VPN, the websites you visit don’t have access to your original IP address, which can be used to derive your approximate location (often not more specific than your city or neighborhood). But that’s not true of the VPN provider themselves - who can identify you much more precisely because you used your VPN login to access the service. Additionally, they can promise not to siphon off your data and write it down somewhere - tracking you, selling it to advertisers, handing it over to law enforcement - but they &lt;em&gt;could&lt;/em&gt; and you’d be none the wiser. By routing all of your traffic through a VPN, &lt;em&gt;you route all of your traffic through a VPN&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Another advantage offered by VPNs is that they can prevent your ISP from knowing what you’re doing online. If you don’t trust your ISP but you do trust your VPN, this makes a lot of sense. It also makes sense if you’re on an unfamiliar network, like airport WiFi. However, it’s still quite important that you &lt;em&gt;do&lt;/em&gt; trust the VPN on the other end. You need to do research. What country are they based in, and what’s their diplomatic relationship with your home country? What kind of power the local authorities have to force them to record &amp; disclose your traffic? Are they backed by venture capitalists who expect infinite growth, and will they eventually have to meet those demands by way of selling your information to advertisers? What happens to you when their business is going poorly? How much do you trust their security competency - are they likely to be hacked? If you haven’t answered all of these questions yourself, then you should not use a VPN.&lt;/p&gt;&lt;p&gt;Even more alarming than the large advertising campaigns which have been popular in the past few months is push-button VPN services which are coming pre-installed on consumer hardware and software. These bother me because they’re implemented by programmers who should understand this stuff and know better than to write the code. Opera now has a push-button VPN pre-bundled which is free and tells you little about the service before happily sending all of your traffic through it.  Do you trust a Chinese web browser’s free VPN to behave in your best interests?  Purism also recently announced a collaboration with Private Internet Access to ship a VPN in their upcoming Librem 5. I consider this highly irresponsible of Purism, and actually discussed the matter at some length with Todd Weaver (the CEO) over email. We need to stop making it easy for users to siphon all of their data into the hands of someone they don’t know.&lt;/p&gt;&lt;p&gt;For anyone who needs a VPN but isn’t comfortable using one of these companies, there are other choices. First, consider that any website you visit with HTTPs support (identified by the little green lock in the address bar on your web browser) is already encrypting all of your traffic so it cannot be read or tampered with. This discloses your IP address to the operator of that website and discloses that you visited that website to your ISP, but does not disclose any data you sent to them, or any content they sent to you, to your ISP or any eavesdroppers. If you’re careful to use HTTPS (and other forms of SSL for things like email), that can often be enough.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;If that’s not enough, the ironclad solution is &lt;a href=&quot;https://www.torproject.org/&quot; target=&quot;_blank&quot;&gt;Tor&lt;/a&gt;. When you connect to a website on Tor, it (1) hides your IP address from the website and any eavesdroppers, (2) hides who you’re talking to from your ISP, and (3) hides what you’re talking about from the ISP. In some cases (onion services), it even hides the origin of the service you’re talking to from &lt;em&gt;you&lt;/em&gt;. Tor comes with its own set of limitations and pitfalls for privacy &amp; security, which you should &lt;a href=&quot;https://2019.www.torproject.org/download/download.html.en#Warning&quot; target=&quot;_blank&quot;&gt;read about and understand&lt;/a&gt; before using it. Bad actors on the Tor network can read and tamper with your traffic if you aren’t using SSL or Onion routing.&lt;/p&gt;&lt;p&gt;Finally, if you have some technical know-how, you can set up your own VPN. If you have a server somewhere (or rent one from a VPS provider), you can install a VPN on it. I suggest &lt;a href=&quot;https://www.wireguard.com/&quot; target=&quot;_blank&quot;&gt;Wireguard&lt;/a&gt; (easiest, Linux only) or &lt;a href=&quot;https://openvpn.net&quot; target=&quot;_blank&quot;&gt;OpenVPN&lt;/a&gt; (more difficult, works on everything). Once again, this comes with its own limitations. You’ll always be using a consistent IP address that services you visit can remember to track you, and you get a new ISP (whoever your VPS provider uses). This’ll generally route you through commercial ISPs, though, who are much less likely to do obnoxious crap like injecting ads in webpages or redirecting your failed DNS queries to “search results” (i.e. more ads). You’ll need to vet your VPS provider and their ISP with equal care.&lt;/p&gt;&lt;p&gt;Understand who handles your data - encrypted and unencrypted - before you share it.  No matter your approach, you should also always install an adblocker (I strongly recommend &lt;a href=&quot;https://github.com/gorhill/uBlock/#installation&quot; target=&quot;_blank&quot;&gt;uBlock Origin&lt;/a&gt;), stick to HTTPS-enabled websites, and be suspicious of and diligent about every piece of software, every browser extension, every app you install, and every website you visit. Most of them are trying to spy on you.&lt;/p&gt;&lt;p&gt;Related articles:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://schub.io/blog/2019/04/08/very-precarious-narrative.html&quot; target=&quot;_blank&quot;&gt;VPN - a Very Precarious Narrative - Dennis Schubert&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.skadligkod.se/vpn/the-trustworthy-of-vpn-review-sites-and-how-affiliate-programs-affects-their-opinion/&quot; target=&quot;_blank&quot;&gt;The trustworthy of VPN review sites and how affiliate programs affects their opinion&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Your-VPN-is-a-serious-choice/</link>
        
        <pubDate>Fri, 19 Apr 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Your-VPN-is-a-serious-choice/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, April 2019</title>
          <description>
            &lt;p&gt;Spring is here, and I’m already miserable in the heat. Crazy weather here in Philadelphia - I was woken up at 3 AM by my phone buzzing, telling me to take immediate shelter from a tornado. But with my A/C cranked up and the tornado safely passed, I’ve been able to get a lot of work done.&lt;/p&gt;&lt;p&gt;The project with the most impressive progress is &lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc2&quot; target=&quot;_blank&quot;&gt;aerc2&lt;/a&gt;. It can now read emails, including filtering them through arbitrary commands for highlighting diffs or coloring quotes, or even rendering HTML email with a TUI browser like w3m.&lt;/p&gt;&lt;script
  id=&quot;asciicast-vy5GmO0tBjppr4G2LSQONIFjH&quot;
  src=&quot;https://asciinema.org/a/pafXXANiWHY9MOH2yXdVHHJRd.js&quot; async
&gt;&lt;/script&gt;
&lt;p&gt;Here’s another demo focusing on the embedded terminal emulator which makes this possible:&lt;/p&gt;&lt;script
  id=&quot;asciicast-N57RaPJqwQD2h0AejLGDWrSi9&quot;
  src=&quot;https://asciinema.org/a/pafXXANiWHY9MOH2yXdVHHJRd.js&quot; async
&gt;&lt;/script&gt;
&lt;p&gt;Keybindings are also working, which are configured simiarly to vim - each keybinding simulates a series of keystrokes, which all eventually boil down to an ex-style command. I’ve bought a domain for aerc, and I’ll be populating it with some marketing content and a nice tour of the features soon. I hope to have time to work on sending emails this month as well. In the immediate future, I need to fix some crashiness that occurs in some situations.&lt;/p&gt;&lt;p&gt;In other email-related news, &lt;a href=&quot;https://git-send-email.io&quot; target=&quot;_blank&quot;&gt;git-send-email.io&lt;/a&gt; is now live, an interactive tutorial on using email with git. This workflow is the one sourcehut focuses on, and is also used by a large number of important free software projects, like Linux, gcc, clang, glibc, musl, ffmpeg, vim, emacs, coreutils… and many, many more. Check it out!&lt;/p&gt;&lt;p&gt;I also spent a fair bit of time working on lists.sr.ht this month. Alpine Linux has provisioned some infrastructure for a likely migration from their current mailing list solution (mlmmj+hypermail) to one based on lists.sr.ht, which I deployed a lists.sr.ht instance to for them, and trained them on some administrative aspects of lists.sr.ht. User-facing improvments that came from this work include tools for importing and exporting mail spools from lists, better access controls, moderation tools, and per-list mime whitelisting and blacklisting. Admin-facing tools include support for a wider variety of MTA configurations and redirects to continue supporting old incoming mail addresses when migrating from another mailing list system.&lt;/p&gt;&lt;p&gt;Stepping outside the realm of email, let’s talk about Wayland. Since Sway 1.0, development has continued at a modest pace, fixing a variety of small bugs and further improving i3 compatibility. We’re getting ready to split swaybg into a standalone project which can be used on other Wayland compositors soon, too. I also have been working more on Godot, and have switched gears towards adding a Wayland backend to Godot upstream - so you can play Godot-based video games on Wayland. I’m still working with upstream and some other interested contributors on the best way to integrate these changes upstream, but I more or less completed a working port with support for nearly all of Godot’s platform abstractions.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://sr.ht/fOvB.png&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://sr.ht/fOvB.png&quot;&gt;
&lt;figcaption&gt;Godot editor running on Wayland with HiDPI support&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;In smaller project news, I spent an afternoon putting together a home-grown video livestreaming platform a few weeks ago. The result: &lt;a href=&quot;https://live.drewdevault.com&quot; target=&quot;_blank&quot;&gt;live.drewdevault.com&lt;/a&gt;. Once upon a time I was livestreaming programming sessions on Twitch.tv, and in the future I’d like to do this more often on my new platform. This one is open source and built on the shoulders of free software tools. I announce new streams on &lt;a href=&quot;https://cmpwn.com/@sir&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;, join us for the next one!&lt;/p&gt;&lt;p&gt;I’m also starting on another project called cozy, which is yak-shaving for several other projects I have in mind. It’s kind of ambitious… it’s a full end-to-end C compiler toolchain. One of my goals (which, when completed, can unblock other tasks before cozy as a whole is done) is to make the parser work as a standalone library for reading, writing, and maniuplating the C AST. I’ve completed the lexer and basic yacc grammar, and I’m working on extracting an AST from the parser. I only started this weekend, so it’s pretty early on.&lt;/p&gt;&lt;p&gt;I’ll leave you with a fun weekend project I did shortly after the last update: &lt;a href=&quot;https://qlock.drewdevault.com/&quot; target=&quot;_blank&quot;&gt;otaqlock&lt;/a&gt;. The server this runs on isn’t awash with bandwidth and the site doesn’t work great on mobile - so your milage may vary - but it is a cool artsy restoration project nonetheless. Until next time, and thank you for your support!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-April-2019/</link>
        
        <pubDate>Mon, 15 Apr 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-April-2019/</guid>
      </item>
    
      <item>
        
        
          <title>Announcing first-class Mercurial support on Sourcehut</title>
          <description>
            &lt;p&gt;I’m pleased to announce that the final pieces have fallen into place for &lt;a href=&quot;https://www.mercurial-scm.org/&quot; target=&quot;_blank&quot;&gt;Mercurial&lt;/a&gt; support on &lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;SourceHut&lt;/a&gt;, which is now on-par with our git offering. Special thanks are owed to SourceHut contributor Ludovic Chabant, who has been instrumental in adding Mercurial support to SourceHut. You may have heard about it while this was still experimental - but I’m happy to tell you that we have now completely integrated Mercurial support into SourceHut! Want to try it out? Check out &lt;a href=&quot;https://man.sr.ht/tutorials/set-up-account-and-hg.md&quot; target=&quot;_blank&quot;&gt;the tutorial&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Mercurial support on SourceHut includes all of the trimmings, including CI support via &lt;a href=&quot;https://builds.sr.ht&quot; target=&quot;_blank&quot;&gt;builds.sr.ht&lt;/a&gt; and email-driven collaboration on &lt;a href=&quot;https://lists.sr.ht&quot; target=&quot;_blank&quot;&gt;lists.sr.ht&lt;/a&gt;. Of course, it’s also 100% free-as-in-freedom, open source software (&lt;a href=&quot;https://hg.sr.ht/~sircmpwn/hg.sr.ht&quot; target=&quot;_blank&quot;&gt;hosted on itself&lt;/a&gt;) that you can &lt;a href=&quot;https://man.sr.ht/hg.sr.ht/installation.md&quot; target=&quot;_blank&quot;&gt;deploy on your own servers&lt;/a&gt;. We’ve tested hg.sr.ht on some of the largest Mercurial repositories out there, including mozilla-central and NetBSD src. The NetBSD project in particular has been very helpful, walking us through their CVS to Hg conversion and stress-testing hg.sr.ht with the resulting giant repositories. I’m looking forward to working more with them in the future!&lt;/p&gt;&lt;p&gt;The Mercurial community is actively innovating their software, and we’ll be right behind them. I’m excited to provide a platform for elevating the Mercurial community. There weren’t a lot of good options for Mercurial fans before SourceHut. Let’s fix that together! SourceHut will be taking a more active role in the Hg community, just like we have for git, and together we’ll build a great platform for software development.&lt;/p&gt;&lt;p&gt;I’ll see you in Paris in May, at the &lt;a href=&quot;https://www.mercurial-scm.org/pipermail/mercurial/2019-April/051196.html&quot; target=&quot;_blank&quot;&gt;inaugural Mercurial conference&lt;/a&gt;!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Hg support on SourceHut was largely written by members of the Mercurial community. If there are other version control communities interested in SourceHut support, please &lt;a href=&quot;mailto:~sircmpwn/sr.ht-dev@lists.sr.ht&quot; target=&quot;_blank&quot;&gt;reach out&lt;/a&gt;!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Announcing-first-class-hg-support-on-sourcehut/</link>
        
        <pubDate>Mon, 15 Apr 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Announcing-first-class-hg-support-on-sourcehut/</guid>
      </item>
    
      <item>
        
        
          <title>NewPipe represents the best of FOSS</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://newpipe.schabi.org/&quot; target=&quot;_blank&quot;&gt;NewPipe&lt;/a&gt; is a free and open-source Android application for browsing &amp; watching YouTube. In my opinion, NewPipe is a perfect case-study in why free &amp; open source software is great and how our values differ from proprietary software in important ways. There’s one simple reason: it’s better than the proprietary YouTube app, in every conceivable way, for free.&lt;/p&gt;&lt;p&gt;NewPipe is better because it’s user-centric software. It exists to make its users lives better, not to enrich its overseers. Because of this, NewPipe has many features which are deliberately omitted from the proprietary app, such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;No advertisements&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;Playing any video in the background&lt;/li&gt;&lt;li&gt;Downloading videos (or their audio tracks alone) to play offline&lt;/li&gt;&lt;li&gt;Playing videos in a pop-up player&lt;/li&gt;&lt;li&gt;Subscribing to channels without a YouTube account&lt;/li&gt;&lt;li&gt;Importing and exporting subscriptions&lt;/li&gt;&lt;li&gt;Showing subscriptions in chronological order&lt;/li&gt;&lt;li&gt;It supports streaming services other than YouTube!&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;YouTube supports some of these… for $12/month. Isn’t that a bit excessive? Other features it doesn’t support at all. On top of that, YouTube is constantly gathering data about you and making decisions which put their interests ahead of yours, whereas NewPipe never phones home and consistently adds new features that put users first. The proprietary app is &lt;em&gt;exploitative&lt;/em&gt; of users, and NewPipe is &lt;em&gt;empowering&lt;/em&gt; users.&lt;/p&gt;&lt;p&gt;There are a lot of political and philosophical reasons to use &amp; support free and open source software. Sometimes it’s hard to get people on board with FOSS by pitching them these first. NewPipe is a great model because it’s straight up &lt;em&gt;better&lt;/em&gt;, and better for reasons that make these philosophical points obvious and poignant. The NewPipe project was started by &lt;a href=&quot;https://schabi.org/&quot; target=&quot;_blank&quot;&gt;Christian Schabesberger&lt;/a&gt;, is co-maintained by a &lt;a href=&quot;https://github.com/orgs/TeamNewPipe/people&quot; target=&quot;_blank&quot;&gt;team of 6&lt;/a&gt;, and has been contributed to by over &lt;a href=&quot;https://github.com/TeamNewPipe/NewPipe/graphs/contributors&quot; target=&quot;_blank&quot;&gt;300 people&lt;/a&gt;. You can donate &lt;a href=&quot;https://newpipe.schabi.org/donate/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.  NewPipe represents the best of our community. Thanks! &lt;img src=&quot;/img/heart.png&quot; style=&quot;display: inline; width: 1.2rem; top: 0.2rem; position: relative&quot; /&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/NewPipe-represents-the-best-of-FOSS/</link>
        
        <pubDate>Tue, 02 Apr 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/NewPipe-represents-the-best-of-FOSS/</guid>
      </item>
    
      <item>
        
        
          <title>Rust is not a good C replacement</title>
          <description>
            &lt;p&gt;I have a saying that summarizes my opinion of Rust compared to Go: “Go is the result of C programmers designing a new programming language, and Rust is the result of C++ programmers designing a new programming language”. This isn’t just a metaphor - Go was designed by plan9 alumni, an operating system written in C and the source of inspiration for many of Go’s features, and Rust was designed by the folks at Mozilla - whose flagship product is one of the largest C++ codebases in the world.&lt;/p&gt;&lt;p&gt;The values of good C++ programmers are incompatible with the values of good C programmers&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Rust is a decent C++ replacement if you have the same goals as C++, but if you don’t, the design has very similar drawbacks. Both Rust and C++ are what I like to call “kitchen sink” programming languages, with the obvious implication. These languages solve problems by adding more language features. A language like C solves problems by writing more C code.&lt;/p&gt;&lt;p&gt;I did some back of the napkin estimates of the rate at which these languages become more complex, based on the rate at which they add features per year. My approach wasn’t very scientific, but I’m sure the point comes across.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;C: 0.73 new features per year&lt;/strong&gt;, measured by the number of bullet points in the C11 article on Wikipedia which summarizes the changes from C99, adjusted to account for the fact that C18 introduced no new features.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Go: 2 new features per year&lt;/strong&gt;, measured by the number of new features listed on the Wikipedia summary of new Go versions.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;C++: 11.3 new features per year&lt;/strong&gt;, measured by the number of bullet points in the C++17 article which summarizes the changes from C++14.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Rust: 15 new features per year&lt;/strong&gt;, measured by the number of headers in the release notes of major Rust versions over the past year, minus things like linters.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This speaks volumes to the stability of these languages, but more importantly it speaks to their complexity. Over time it rapidly becomes difficult for one to keep an up-to-date mental map of Rust and how to solve your problems idiomatically. A Rust program written last year already looks outdated, whereas a C program written ten years ago has pretty good odds of being just fine. Systems programmers don’t want shiny things - we just want things that work. That really cool feature $other_language has? Not interested. It’ll be more trouble than it’s worth.&lt;/p&gt;&lt;p&gt;With the philosophical wish-wash out of the way and the tone set, let me go over some more specific problems when considering Rust as a C replacement.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;C is the most portable programming language&lt;/strong&gt;. Rust actually has a pretty admirable selection of supported targets for a new language (thanks mostly to LLVM), but it pales in comparison to C, which runs on almost &lt;em&gt;everything&lt;/em&gt;. A new CPU architecture or operating system can barely be considered to exist until it has a C compiler. And once it does, it unlocks access to a vast repository of software written in C. Many other programming languages, such as Ruby and Python, are implemented in C and you get those for free too.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;C has a spec&lt;/strong&gt;. No spec means there’s nothing keeping rustc honest. Any behavior it exhibits could change tomorrow. Some weird thing it does could be a feature &lt;em&gt;or&lt;/em&gt; a bug. There’s no way to know until your code breaks. That they can’t slow down to pin down exactly what defines Rust is also indicative of an immature language.&lt;/p&gt;&lt;iframe
  src=&quot;https://cmpwn.com/@sir/100437209244243864/embed&quot;
  class=&quot;mastodon-embed&quot;
  style=&quot;max-width: 100%; border: 0; margin: 0 auto; display: block;&quot;
  width=&quot;400&quot;&gt;&lt;/iframe&gt;
&lt;script src=&quot;https://cmpwn.com/embed.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;strong&gt;C has many implementations&lt;/strong&gt;. C has many competing compilers. They all work together stressing out the spec, fishing out the loosely defined corners, and pinning down exactly what C is. Code that compiles in one and not another is indicative of a bug in one of them, which gives a nice extra layer of testing to each. By having many implementations, we force C to be well defined, and this is good for the language and its long-term stability. Rustc could stand to have some competition as well, maybe it would get faster!&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;C has a consistent &amp; stable ABI&lt;/strong&gt;. The System-V ABI is supported on a wide variety of systems and has been mostly agreed upon by now. Rust, on the other hand, has no stable internal ABI. You have to compile and link everything all in one go on the same version of the Rust compiler. The only code which can interact with the rest of the ecosystem is unidiomatic Rust, written at some kind of checkpoint between Rust and the outside world. The outside world exists, it speaks System-V, and us systems programmers spend a lot of our time talking to it.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Cargo is mandatory&lt;/strong&gt;. On a similar line of thought, Rust’s compiler flags are not stable. Attempts to integrate it with other build systems have been met with hostility from the Rust &amp; Cargo teams. The outside world exists, and us systems programmers spend a lot of our time integrating things. Rust refuses to play along.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Concurrency is generally a bad thing.&lt;/strong&gt; Serial programs have X problems, and parallel programs have X&lt;span class=&quot;superscript &quot;&gt;Y&lt;/span&gt; problems, where Y is the amount of parallelism you introduce. Parallelism in C is a pain in the ass for sure, and this is one reason I find Go much more suitable to those cases. However, nearly all programs needn’t be parallel. A program which uses poll effectively is going to be simpler, reasonably performant, and have orders of magnitude fewer bugs. “Fearless concurrency” allows you to fearlessly employ bad software design 9 times out of 10.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Safety&lt;/strong&gt;. Yes, Rust is more safe. I don’t really care. In light of all of these problems, I’ll take my segfaults and buffer overflows. I especially refuse to “rewrite it in Rust” - because no matter what, rewriting an entire program from scratch is &lt;em&gt;always&lt;/em&gt; going to introduce more bugs than maintaining the C program ever would. I don’t care what language you rewrite it in.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;C is far from the perfect language - it has many flaws. However, its replacement will be simpler - not more complex. Consider Go, which has had a lot of success in supplanting C for many problems. It does this by specializing on certain classes of programs and addressing them with the simplest solution possible. It hasn’t completely replaced C, but it has made a substantial dent in its problem space - more than I can really say for Rust (which has made similar strides for C++, but definitely not for C).&lt;/p&gt;&lt;p&gt;The kitchen sink approach doesn’t work. Rust will eventually fail to the “jack of all trades, master of none” problem that C++ has. Wise languages designers start small and stay small. Wise systems programmers extend this philosophy to designing entire systems, and Rust is probably not going to be invited. I understand that many people, particularly those already enamored with Rust, won’t agree with much of this article. But now you know why we are still writing C, and hopefully you’ll stop bloody bothering us about it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Rust-is-not-a-good-C-replacement/</link>
        
        <pubDate>Mon, 25 Mar 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Rust-is-not-a-good-C-replacement/</guid>
      </item>
    
      <item>
        
        
          <title>Status update, March 2019</title>
          <description>
            &lt;p&gt;My todo list is getting completed at a pace it’s never seen before, and growing at a new pace, too. This full-time FOSS gig is really killing it! As the good weather finally starts to roll in, it’s time for March’s status update. Note: I posted updates &lt;a href=&quot;https://www.patreon.com/sircmpwn/posts&quot; target=&quot;_blank&quot;&gt;on Patreon&lt;/a&gt; before, but will start posting here instead. This medium doesn’t depend on a proprietary service, allows for richer content, and is useful for my supporters who support my work via other donation platforms.&lt;/p&gt;&lt;p&gt;Sway 1.0 has been released! I wrote a &lt;a href=&quot;https://drewdevault.com/blog/Sway-1.0-released/&quot;&gt;detailed write-up&lt;/a&gt; on the release and our future plans separately, which I encourage you to read if you haven’t already. However, I do have some additional progress to share outside of the big sway 1.0 news. In the last update, I mentioned that I got a Librem 5 devkit from Guido Günther&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; at FOSDEM. My plans were to get this up and running with sway and start improving touch support, and I’ve accomplished both:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/fGxf.jpg&quot;&gt;
&lt;figcaption&gt;A picture of a Librem5 devkit running pmOS and sway&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;As you can see, I also got &lt;a href=&quot;https://postmarketos.org/&quot; target=&quot;_blank&quot;&gt;postmarketOS&lt;/a&gt; running, and I love it - I hope to work with them a lot in the future. The &lt;a href=&quot;https://github.com/swaywm/sway/pull/3711&quot; target=&quot;_blank&quot;&gt;first patch&lt;/a&gt; for improving touch support in sway has landed and I’ll be writing more in the future. I also sent some patches to Purism’s &lt;a href=&quot;https://source.puri.sm/Librem5/virtboard&quot; target=&quot;_blank&quot;&gt;virtboard&lt;/a&gt; project, an on-screen keyboard, making it more useful for Sway users. I hope to make an OSK of my own at some point, with multiple layouts, CJK support, and client-aware autocompletion, in the future. Until then, an improved virtboard is a nice stop-gap :) I’ve also been working on wlroots a bit, including &lt;a href=&quot;https://github.com/swaywm/wlroots/pull/1578&quot; target=&quot;_blank&quot;&gt;a patch adding remote desktop support&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;In other Wayland news, I’ve also taken a part time contract to build a module integrating wlroots with the &lt;a href=&quot;https://godotengine.org/&quot; target=&quot;_blank&quot;&gt;Godot game engine&lt;/a&gt;: &lt;a href=&quot;https://git.sr.ht/~sircmpwn/gdwlroots&quot; target=&quot;_blank&quot;&gt;gdwlroots&lt;/a&gt;. The long-term goal is to build a VR compositor based on Godot and develop standards for Wayland applications to have 3D content. 100% of this work is free software (MIT licensed) and will bring improvements to both the wlroots and Godot ecosystems. Next week I’ll be starting work on adding a Wayland backend to Godot so that Godot-based games can run on Wayland compositors directly. Here’s an example compositor running on Godot:&lt;/p&gt;&lt;video
  src=&quot;/l.sr.ht/9bV-.webm&quot;
  autoplay muted loop controls
  style=&quot;max-width: 100%;&quot;
&gt;&lt;/video&gt;
&lt;p&gt;I’ve also made some significant progress on &lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc2&quot; target=&quot;_blank&quot;&gt;aerc2&lt;/a&gt;. I have fleshed out the command subsystem, rigged up keybindings, and implemented the message list, and along with it all of the asynchronous communication between the UI thread, network thread, and mail server. I think at this point most of the unknowns are solved with aerc2, and the rest just remains to be implemented. I’m glad I chose to rewrite it from C, though my love for C still runs deep. The Go ecosystem is much better suited to the complex problems and dependency tree of software like aerc, plus has a nice concurrency model for aerc’s async design.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; The next major problem to address is the embedded terminal emulator, which I hope to start working on soon.&lt;/p&gt;&lt;script
  id=&quot;asciicast-pafXXANiWHY9MOH2yXdVHHJRd&quot;
  src=&quot;https://asciinema.org/a/pafXXANiWHY9MOH2yXdVHHJRd.js&quot; async
&gt;&lt;/script&gt;
&lt;p&gt;aerc2’s progress is a great example of my marginalized projects becoming my side projects, as my side projects become my full-time job, and thus all of them are developing at a substantially improved pace. The productivity increase is pretty crazy. I’m really thankful to everyone who’s supporting my work, and excited to keep building crazy cool software thanks to you.&lt;/p&gt;&lt;p&gt;I was meaning to work on RISC-V this month, but I’ve been a little bit distracted by everything else. However, there has been some discussion about how to approach upstreaming and I’m planning on tackling this next week. I also spent some time putting together a custom 1U I can install in my datacenter for a more permanent RISC-V setup. Some of this is working towards getting RISC-V ready for builds.sr.ht users to take advantage of - that relay is for cutting power to the board to force a reboot when it misbehaves - but a lot of this is also useful for my own purposes in porting musl &amp; Alpine Linux.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/M7me.jpg&quot;&gt;
&lt;figcaption&gt;Picture of a 1U chassis with a bunch of custom components within&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;One problem I’m still trying to solve is the microSD card. I don’t want to run untrusted builds.sr.ht code when that microSD card is plugged in. I’ve been working on some prototyping (breaking out the old soldering iron) to make a microSD… thing, which I can plug into this and physically cut VCC to the microSD card with that relay I have rigged up. This is pretty hard, and my initial attempts were unsuccessful. If anyone knowledgable about this has ideas, please get in touch.&lt;/p&gt;&lt;p&gt;Outside of RISC-V, I have been contributing to Alpine Linux a lot more lately in general. I adopted the sway &amp; wlroots packages, have been working on improved PyQt support, cleaning up Python packages, clearing out the nonfree MongoDB packages, and more. I also added a bunch of new packages for miscellaneous stuff, including alacritty, font-noto-cjk, nethack, and Simon Ser’s &lt;a href=&quot;https://github.com/emersion/go-dkim&quot; target=&quot;_blank&quot;&gt;go-dkim&lt;/a&gt; milter. Most importantly, however, I’ve started &lt;a href=&quot;https://wiki.alpinelinux.org/wiki/Python_package_policies&quot; target=&quot;_blank&quot;&gt;planning&lt;/a&gt; and &lt;a href=&quot;https://lists.alpinelinux.org/alpine-devel/6465.html&quot; target=&quot;_blank&quot;&gt;discussing&lt;/a&gt; a Python overhaul project in aports with the Alpine team, which will including cleaning up all of the Python patches and starting on Python 2 removal. I depend a lot on Alpine and its Python support, so I’m excited to be working on these improvements!&lt;/p&gt;&lt;p&gt;I have some Sourcehut news as well. Like usual, there’ll be a detailed Sourcehut-specific update posted to the &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-announce&quot; target=&quot;_blank&quot;&gt;sr.ht-announce&lt;/a&gt; mailing list later on. With Ludovic Chabant’s help, there have been continued improvements to Mercurial support, notably adding builds.sr.ht integration as of yesterday. Thanks Ludovic! We’ve also been talking to some NetBSD folks who may be interested in using Sourcehut to host the NetBSD code once they finish their CVS-&gt;Hg migration, and we’ve been improving the performance for large repositories during their experiments on sr.ht.&lt;/p&gt;&lt;p&gt;There’s a bunch more going on with Sourcehut - paste.sr.ht, APIs, a command line interface for those APIs, webhooks, and more still - check out the email on sr.ht-announce later. That’s all I have for you today. Thank you for your support, and until next time!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Status-update-March-2019/</link>
        
        <pubDate>Fri, 15 Mar 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Status-update-March-2019/</guid>
      </item>
    
      <item>
        
        
          <title>Announcing the release of sway 1.0</title>
          <description>
            &lt;p&gt;1,315 days after I started the &lt;a href=&quot;https://swaywm.org&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt; project, it’s finally time for &lt;a href=&quot;https://github.com/swaywm/sway/releases/tag/1.0&quot; target=&quot;_blank&quot;&gt;sway 1.0&lt;/a&gt;! I had no idea at the time how much work I was in for, or how many talented people would join and support the project with me. In order to complete this project, we have had to rewrite the entire Linux desktop nearly from scratch. Nearly 300 people worked together, together writing over 9,000 commits and almost 100,000 lines of code, to bring you this release.&lt;/p&gt;&lt;p&gt;&lt;small class=&quot;text-muted&quot;&gt;Sway is an i3-compatible Wayland desktop for Linux and FreeBSD&lt;/small&gt;&lt;/p&gt;&lt;p&gt;1.0 is the first stable release of sway and represents a consistent, flexible, and powerful desktop environment for Linux and FreeBSD. We hope you’ll enjoy it! If the last sway release you used was 0.15 or earlier, you’re in for a shock. 0.15 was a buggy, frustrating desktop to use, but sway 1.0 has been completely overhauled and represents a much more capable desktop. It’s almost impossible to summarize all of the changes which makes 1.0 great. Sway 1.0 adds a huge variety of features which were sorely missed on 0.x, improves performance in every respect, offers a more faithful implementation of Wayland, and exists as a positive political force in the Wayland ecosystem pushing for standardization and cooperation among Wayland projects.&lt;/p&gt;&lt;p&gt;When planning the future of sway, we realized that the Wayland ecosystem was sorely in need of a stable &amp; flexible common base library to encapsulate all of the difficult and complex facets of building a desktop. To this end, I decided we would build &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;. It’s been a smashing success. This project has become very important to the Linux desktop ecosystem, and the benefits we reap from it have been shared with the community at large. &lt;a href=&quot;https://github.com/swaywm/wlroots/wiki/Projects-which-use-wlroots&quot; target=&quot;_blank&quot;&gt;Dozens of projects&lt;/a&gt; are using it today, and soon you’ll find it underneath most Linux desktops, on your phone, in your VR environment, and more. Its influence extends beyond its borders as well, as we develop and push for standards throughout Wayland.&lt;/p&gt;&lt;p&gt;Through this work we have also helped to build a broader ecosystem of tools built on interoperable standards which you may find useful in your new sway 1.0 desktop. Here are a few of my favorites - each of which is compatible with many Wayland compositors:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/swaywm/swayidle&quot; target=&quot;_blank&quot;&gt;swayidle&lt;/a&gt;: idle management daemon&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/swaywm/swaylock&quot; target=&quot;_blank&quot;&gt;swaylock&lt;/a&gt;: lock screen&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/emersion/mako&quot; target=&quot;_blank&quot;&gt;mako&lt;/a&gt;: notification daemon&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/emersion/grim&quot; target=&quot;_blank&quot;&gt;grim&lt;/a&gt;: screenshot tool&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/emersion/slurp&quot; target=&quot;_blank&quot;&gt;slurp&lt;/a&gt;: interactive region selection&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/ammen99/wf-recorder&quot; target=&quot;_blank&quot;&gt;wf-recorder&lt;/a&gt;: video capture tool&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Alexays/Waybar&quot; target=&quot;_blank&quot;&gt;waybar&lt;/a&gt;: alternative panel&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://source.puri.sm/Librem5/virtboard&quot; target=&quot;_blank&quot;&gt;virtboard&lt;/a&gt;: on-screen keyboard&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/bugaevc/wl-clipboard&quot; target=&quot;_blank&quot;&gt;wl-clipboard&lt;/a&gt;: xclip replacement&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/xyproto/wallutils&quot; target=&quot;_blank&quot;&gt;wallutils&lt;/a&gt;: fancy wallpaper manager&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;p&gt;None of this would be possible without the support of sway’s and wlroots’ talented contributors. Hundreds of people worked together on this. I’d like to give special thanks to our core contributors: Brian Ashworth, Ian Fan, Ryan Dwyer, Scott Anderson, and Simon Ser. Thanks are also in order for those who have helped wlroots fit into the broader ecosystem - thanks to Purism for their help on wlroots, KDE &amp; Canonical for their help on protocol standardization. I also owe thanks to all of the other projects which use wlroots, particularly including Way Cooler, Wayfire, and Waymonad, who all have made substantial contributions to wlroots in their pursit of the best Wayland desktop.&lt;/p&gt;&lt;p&gt;I’d also of course like to thank all of the users who have donated to support my work, which I now do full-time, which has had and I hope will continue to have a positive impact on the project and those around it. Please consider &lt;a href=&quot;https://drewdevault.com/donate&quot; target=&quot;_blank&quot;&gt;donating&lt;/a&gt; to support the future of sway &amp; wlroots if you haven’t yet.&lt;/p&gt;&lt;p&gt;Though sway today is already stable and powerful, we’re not done yet. We plan to continue improving performance &amp; stability, adding useful desktop features, taking advantage of better hardware, and bringing sway to more users. Here’s some of what we have planned for future releases:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Better Wayland-native tools for internationalized input methods like CJK&lt;/li&gt;&lt;li&gt;Better accessibility tools including improved screen reader support, high-contrast mode, a magnifying glass tool, and so on&lt;/li&gt;&lt;li&gt;Integration with xdg-portal &amp; pipewire for interoperable screen capture&lt;/li&gt;&lt;li&gt;Improved touch screen support for use on the &lt;a href=&quot;https://puri.sm/products/librem-5/&quot; target=&quot;_blank&quot;&gt;Librem 5&lt;/a&gt; and on &lt;a href=&quot;https://postmarketos.org/&quot; target=&quot;_blank&quot;&gt;postmarketOS&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Better support for drawing tablets and additional hardware&lt;/li&gt;&lt;li&gt;Sandboxing and security features&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;As with all sway features, we intend to have the best-in-class implementations of these features and set the bar as high as we can for everyone else. We’re looking forward to your continued support!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Sway-1.0-released/</link>
        
        <pubDate>Mon, 11 Mar 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Sway-1.0-released/</guid>
      </item>
    
      <item>
        
        
          <title>Sourcehut&apos;s spartan approach to web design</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://sourcehut.org&quot; target=&quot;_blank&quot;&gt;Sourcehut&lt;/a&gt; is known for its brutalist design, with its mostly shades-of-gray appearance, conservative color usage, and minimal distractions throughout. This article aims to share some insights into the philosophy that guides this design, both for the curious reader and for the new contributor to the open-source project.&lt;/p&gt;&lt;p&gt;The most important principle is that sr.ht is an engineering tool first and foremost, and when you’re there it’s probably because you’re in engineering mode. Therefore, it’s important to bring the information you’re there for to the forefront, and minimize distractions. In practice, this means that the first thing on any page to grab your attention should be the thing that brought you there. Consider &lt;a href=&quot;https://git.sr.ht/~sircmpwn/git.sr.ht/tree/master/gitsrht/service.py&quot; target=&quot;_blank&quot;&gt;the source file view on git.sr.ht&lt;/a&gt;. For reference, here are similar pages on &lt;a href=&quot;https://github.com/torvalds/linux/blob/master/init/main.c&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; and &lt;a href=&quot;https://gitlab.freedesktop.org/libinput/libinput/blob/master/src/evdev.c&quot; target=&quot;_blank&quot;&gt;Gitlab&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/git.sr.ht/tree/master/gitsrht/service.py&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/kkJm.png&quot;&gt;
&lt;figcaption&gt;Screenshot of git.sr.ht&lt;/figcaption&gt;&lt;/figure&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The vast majority of the git.sr.ht page is dedicated to the source code we’re reading here, and it’s also where most of the colors are. Your eye is drawn straight to the content. Any additional information we show on this page is directly relevant to the content: breadcrumbs to other parts of the tree, file mode &amp; size, links to other views on this repository. The nav can take you away from this page, but it’s colored a light grey to avoid being distracting and each link is another engineering tool - no marketing material or fluff. Contrast with GitHub: a large, dark, attention grabbing navbar with links to direct you away from the content and towards marketing pages. If you’re logged out, you get a giant sign-up box which pushes the content halfway off the page. Colors here are also distracting: note the large line of colorful avatars that catches your eye despite almost certainly being unrelated to your purpose on this page.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/1qdZ.png&quot;&gt;
&lt;figcaption&gt;Screenshot of builds.sr.ht&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Colors are used much more conservatively on sourcehut. If you log into builds.sr.ht and visit the index page, you’re greeted with a large blue “submit manifest” button, and very little color besides. This is probably why you were here - so it’s made obvious and colorful so your eyes can quickly find it and get on with your work. Other pages are similar: the todo.sr.ht tracker page has a large form with a blue “submit” button for creating a new ticket, email views on lists.sr.ht have a large blue “reply to thread” button, and &lt;a href=&quot;https://man.sr.ht&quot; target=&quot;_blank&quot;&gt;man.sr.ht&lt;/a&gt; has a large green button enticing new users towards the tutorials. Red is also used throughout for dangerous actions, like deleting things.  Each button also is unambiguous and relies on the text within itself rather than the text nearby: the git.sr.ht repository deletion page uses “Delete $reponame”, rather than “Continue”.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/d6Vx.png&quot;&gt;
&lt;figcaption&gt;Screenshot of repo deletion UI&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;The last important point in sourcehut’s design is the use of icons, or rather the lack thereof. Icons are used extremely conservatively on sr.ht. Interactive icons (things you are expected to click) are never shown without text that clarifies what happens when you click them. Informational icons usually have a tooltip which explains their meaning, and are quite rare - only used in cases where real estate limits the use of text. Assigning an icon to every action or detail is not necessary and would add more distractions to the screen.&lt;/p&gt;&lt;p&gt;I’m not a particularly skilled UI designer, so keeping it simple like this also helps to make a reasonably nice UI attainable for an engineer-oriented developer like me. Adding new pages is generally easy and requires little thought by applying these basic principles throughout, and the simple design doesn’t take long to execute on. It’s not perfect, but I like it and I’ve received positive feedback from my users.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/sourcehut-design/</link>
        
        <pubDate>Mon, 04 Mar 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/sourcehut-design/</guid>
      </item>
    
      <item>
        
        
          <title>Tips for a disciplined git workflow</title>
          <description>
            &lt;p&gt;Basic git usage involves typing a few stock commands to “&lt;a href=&quot;https://xkcd.com/1597/&quot; target=&quot;_blank&quot;&gt;sync everyone up&lt;/a&gt;”. Many people who are frustrated with git become so because they never progress beyond this surface-level understanding of how it works. However, mastering git is easily worth your time. How much of your day is spent using git? I would guess that there are many tools in your belt that you use half as often and have spent twice the time studying.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/1205/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/is_it_worth_the_time.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If you’d like to learn more about git, I suggest starting with &lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain&quot; target=&quot;_blank&quot;&gt;Chapter 10&lt;/a&gt; of &lt;a href=&quot;https://git-scm.com/book/en/v2&quot; target=&quot;_blank&quot;&gt;Pro Git&lt;/a&gt; (it’s free!), then reading chapters 2, 3, and 7. The rest is optional. In this article, we’re going to discuss how you can apply the tools discussed in the book to a disciplined and productive git workflow.&lt;/p&gt;&lt;h3&gt;The basics: Writing good commit messages&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/1296/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/git_commit.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You may have heard this speech before, but bear with me. Generally, you should not use &lt;code&gt;git commit -m &amp;quot;Your message here&amp;quot;&lt;/code&gt;. Start by configuring git to use your favorite editor: &lt;code&gt;git config --global core.editor vim&lt;/code&gt;, then simply run &lt;code&gt;git commit&lt;/code&gt; alone. Your editor will open and you can fill in the file with your commit message. The first line should be limited to 50 characters in length, and should complete this sentence: when applied, this commit will… “Fix text rendering in CJK languages”. “Add support for protocol v3”. “Refactor CRTC handling”. Then, add a single empty line, and expand on this in the &lt;em&gt;extended commit description&lt;/em&gt;, which should be hard-wrapped at 72 columns, and include details like rationale for the change, tradeoffs and limitations of the approach, etc.&lt;/p&gt;&lt;p&gt;We use 72 characters because that’s &lt;a href=&quot;https://tools.ietf.org/html/rfc2822#section-2.1.1&quot; target=&quot;_blank&quot;&gt;the standard width of an email&lt;/a&gt;, and email is an important tool for git. The 50 character limit is used because the first line becomes the subject line of your email - and lots of text like “&lt;code&gt;[PATCH linux-usb v2 0/13]&lt;/code&gt;” can get added to beginning. You might find wrapping your lines like this annoying and burdensome - but consider that when working with others, they may not be reading the commit log in the same context as you. I have a vertical monitor that I often read commit logs on, which is not going to cram as much text into one line as your 4K 16:9 display could.&lt;/p&gt;&lt;h3&gt;Each commit should be a self-contained change&lt;/h3&gt;&lt;p&gt;Every commit should only contain one change - avoid sneaking in little unrelated changes into the same commit&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Additionally, avoid breaking one change into several commits, unless you can refactor the idea into discrete steps - each of which represents a complete change in its own right. If you have several changes in your working tree and only need to commit some of them, try &lt;code&gt;git add -i&lt;/code&gt; or &lt;code&gt;git add -p&lt;/code&gt;. Additionally, every commit should compile and run all tests successfully, and should avoid having any known bugs which will be fixed up in a future commit.&lt;/p&gt;&lt;p&gt;If this is true of your repository, then you can check out any commit and expect the code to work correctly. This also becomes useful later, for example when cherry-picking commits into a release branch. Using this approach also allows &lt;a href=&quot;https://git-scm.com/docs/git-bisect&quot; target=&quot;_blank&quot;&gt;git-bisect&lt;/a&gt; to become more useful&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, because if you can expect the code to compile and complete tests successfully for every commit, you can pass &lt;code&gt;git-bisect&lt;/code&gt; a script which programmatically tests a tree for the presence of a bug and avoid false positives. These self-contained commits with good commit messages can also make it really easy to prepare release notes with &lt;a href=&quot;https://git-scm.com/docs/git-shortlog&quot; target=&quot;_blank&quot;&gt;git-shortlog&lt;/a&gt;, &lt;a href=&quot;https://lkml.org/lkml/2019/1/6/178&quot; target=&quot;_blank&quot;&gt;like Linus does with Linux releases&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;Get it right on the first try&lt;/h3&gt;&lt;p&gt;We now come to one of the most important features of git which distinguishes it from its predecessors: history editing. All version control systems come with a time machine of some sort, but before git they were mostly read-only. However, git’s time machine is different: you can change the past. In fact, you’re encouraged to! But a word of warning: only change a future which has yet to be merged into a stable public branch.&lt;/p&gt;&lt;p&gt;The advice in this article - bug-free, self-contained commits with a good commit message - is hard to get right on the first try. Editing your history, however, is easy and part of an effective git workflow. Familiarize yourself with &lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History&quot; target=&quot;_blank&quot;&gt;git-rebase&lt;/a&gt; and use it liberally. You can use rebase to reorder, combine, delete, edit, and split commits. One workflow I find myself commonly using is to make some changes to a file, commit a “fixup” commit (&lt;code&gt;git commit -m fixup&lt;/code&gt;), then use &lt;code&gt;git rebase -i&lt;/code&gt; to squash it into an earlier commit.&lt;/p&gt;&lt;h3&gt;Other miscellaneous tips&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Read the man pages! Pick a random git man page and read it now. Also, if you haven’t read the top-level git man page (simply &lt;code&gt;man git&lt;/code&gt;), do so.&lt;/li&gt;&lt;li&gt;At the bottom of each man page for a high-level git command is usually a list of low-level git commands that the high-level command relies on. If you want to learn more about how a high-level git command works, try reading these man pages, too.&lt;/li&gt;&lt;li&gt;Learn how to specify the commit you want with &lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection&quot; target=&quot;_blank&quot;&gt;rev selection&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Branches are useful, but you should learn how to work without them as well to have a nice set of tools in your belt. Use tools like &lt;code&gt;git pull --rebase&lt;/code&gt;, &lt;code&gt;git send-email -1 HEAD~2&lt;/code&gt;, and &lt;code&gt;git push origin HEAD~2:master&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Using-git-with-discipline/</link>
        
        <pubDate>Mon, 25 Feb 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Using-git-with-discipline/</guid>
      </item>
    
      <item>
        
        
          <title>Generics aren&apos;t ready for Go</title>
          <description>
            &lt;p&gt;In the distance, a gradual roar begins to grow in volume. A dust cloud is visible over the horizon. As it nears, the shouts of the oncoming angry mob can be heard. Suddenly, it stops, and a brief silence ensues. Then the air is filled with the clackings of hundreds of keyboards, angrily typing the owner’s opinion about generics and Go. The clans of Java, C#, Rust, C++, TypeScript, Haskell, and more - usually mortal enemies - have combined forces to fight in what may become one of the greatest flamewars of our time. And none of them read more than the title of this article before writing their comment.&lt;/p&gt;&lt;p&gt;Have you ever seen someone write something to the effect of “I would use Go, but I need generics”? Perhaps we can infer from this that many of the people who are pining after generics in Go are not, in fact, Go users. Many of them are users of another programming language that &lt;em&gt;does&lt;/em&gt; have generics, and they feel that generics are a good fit for this language, and therefore a good fit for any language. The inertia of “what I’m used to” comes to a violent stop when they try to use Go. People affected by this frustration interpret it as a problem with Go, that Go is missing some crucial feature - such as generics. But this lack of features is itself a feature, not a bug.&lt;/p&gt;&lt;p&gt;Go strikes me as one of the most conservative programming languages available today. It’s small and simple, and every detail is carefully thought out. There are very few dusty corners of Go - in large part because Go has fewer corners in general than most programming languages. This is a major factor in Go’s success to date, in my opinion. Nearly all of Go’s features are bulletproof, and in my opinion are among the best implementations of their concepts in our entire industry. Achieving this feat requires having &lt;em&gt;fewer&lt;/em&gt; features in total. Contrast this to C++, which has too many footguns to count. You &lt;em&gt;could&lt;/em&gt; write a book called “C++: the good parts”, but consider that such a book about Go would just be a book about Go. There’s little room for the bad parts in such a spartan language.&lt;/p&gt;&lt;p&gt;So how should we innovate in Go? Consider the case of dependency management. Go 1.11 shipped with the first version of Go modules, which, in my opinion, is a game changer. I passionately hate &lt;code&gt;$GOPATH&lt;/code&gt;, and I thought dep wasn’t much better. dep’s problem is that it took the dependency management ideas that other programming languages have been working with and brought the same ideas to Go. Instead, Go modules took the idea of dependency management and rethought it from first principles, then landed on a much more elegant solution that I think other programming languages will spend the next few years catching up with. I like to make an analogy to physics: dep is like &lt;a href=&quot;https://en.wikipedia.org/wiki/General_relativity&quot; target=&quot;_blank&quot;&gt;General Relativity&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Standard_Model&quot; target=&quot;_blank&quot;&gt;the Standard Model&lt;/a&gt;, whereas Go modules are more like the &lt;a href=&quot;https://en.wikipedia.org/wiki/Grand_Unified_Theory&quot; target=&quot;_blank&quot;&gt;Grand Unified Theory&lt;/a&gt;. Go doesn’t settle for anything less when adding features. It’s not a language where liberal experimentation with imperfect ideas is desirable.&lt;/p&gt;&lt;p&gt;I feel that this applies to generics. In my opinion, generics are an imperfect solution to an unsolved problem in computer science. None of the proposals I’ve seen (notably &lt;a href=&quot;https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md&quot; target=&quot;_blank&quot;&gt;contracts&lt;/a&gt;) feel &lt;em&gt;right&lt;/em&gt; yet. Some of this is a gut feeling, but there are tangible problems as well. For example, the space of problems they solve intersects with other Go features, which weakens the strength of both features. “Which solution do I use to this problem” is a question which different people will answer differently, and consequently their code at best won’t agree on what “idiomatic” means and at worst will be simply incompatible.  Another problem is that the proposal changes the meaning of idiomatic Go in the first place - suddenly huge swaths of the Go code, including the standard library, will become unidiomatic. One of Go’s greatest strengths is that code written 5 years ago is still idiomatic. It’s almost impossible to write unidiomatic Go code at all.&lt;/p&gt;&lt;p&gt;I used to sneer at the Go maintainers alongside everyone else whenever they’d punt on generics. With so many people pining after it, why haven’t they seen sense yet? How can they know better than all of these people? My tune changed once I started to use Go more seriously, and now I admire their restraint.  Part of this is an evolution of my values as a programmer in general: simplicity and elegance are now the principles I optimize for, even if it means certain classes of programs are simply not on the table. And I think Go should be comfortable not being suitable for writing certain classes of programs. I don’t think programming languages should compete with each other in an attempt to become the perfect solution to every problem.  This is impossible, and attempts will just create a messy kitchen sink that solves every problem poorly.&lt;/p&gt;&lt;img
  src=&quot;/l.sr.ht/TxC_.jpg&quot;
  alt=&quot;Nina Tucker from Fullmetal Alchemist&quot;
  width=&quot;320&quot;&gt;
&lt;p class=&quot;text-center&quot;&gt;
  &lt;small&gt;fig. 1: the result of C++&apos;s attempt to solve all problems&lt;/small&gt;
&lt;/p&gt;
&lt;p&gt;The constraints imposed by the lack of generics (and other things Go lacks) breed creativity.  If you’re fighting Go’s lack of generics trying to do something Your Way, you might want to step back and consider a solution to the problem which embraces the limitations of Go instead. Often when I do this the new solution is a much better design.&lt;/p&gt;&lt;p&gt;So it’s my hope that Go will hold out until the right solution presents itself, and it hasn’t yet. Rushing into it to appease the unwashed masses is a bad idea. There are other good programming languages - use them! I personally use a wide variety of programming languages, and though I love Go dearly, it probably only comes in 3rd or 4th place in terms of how frequently it appears in my projects. It’s &lt;em&gt;excellent&lt;/em&gt; in its domain and doesn’t need to awkwardly stumble into others.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Generics-arent-ready-for-Go/</link>
        
        <pubDate>Mon, 18 Feb 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Generics-arent-ready-for-Go/</guid>
      </item>
    
      <item>
        
        
          <title>Wayland misconceptions debunked</title>
          <description>
            &lt;p&gt;This article has been on my backburner for a while, but it seems Wayland FUD is making the news again recently, so I’ve bumped up the priority a bit. For those new to my blog, I am the maintainer of &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;, a library which implements much of the functionality required of a Wayland compositor and is arguably the single most influential project in Wayland right now; and &lt;a href=&quot;https://swaywm.org&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt;, a popular Wayland compositor which is nearing version 1.0. Let’s go over some of the common misconceptions I hear about Wayland and why they’re wrong. Feel free to pick and choose the misconceptions you believe to read and disregard the rest.&lt;/p&gt;&lt;p&gt;The art of hating Wayland has become a cult affair. We don’t need to put ourselves into camps at war. Please try not to read this article through the lens of anger.&lt;/p&gt;&lt;h3&gt;Wayland isn’t more secure, look at this keylogger!&lt;/h3&gt;&lt;p&gt;There is an &lt;a href=&quot;https://github.com/Aishou/wayland-keylogger&quot; target=&quot;_blank&quot;&gt;unfortunate GitHub project&lt;/a&gt; called “Wayland keylogger” whose mode of operation is using &lt;code&gt;LD_PRELOAD&lt;/code&gt; to intercept calls to the libwayland shared library and record keypresses from it. The problem with this “critique” is stated in the &lt;code&gt;README.md&lt;/code&gt; file, though most don’t read past the title of the repository. Wayland is only &lt;em&gt;one part&lt;/em&gt; of an otherwise secure system. Using &lt;code&gt;LD_PRELOAD&lt;/code&gt; is effectively equivalent to rewriting client programs to log keys themselves, and any program which is in a position to do this has already won. If I rephrased this as “Wayland can be keylogged, assuming the attacker can sneak some evil code into your .bashrc”, the obviousness of this truth should become immediately apparent.&lt;/p&gt;&lt;p&gt;Some people have also told me that they can log keys by opening &lt;code&gt;/dev/input/*&lt;/code&gt; files and reading input events. They’re right! Try it yourself: &lt;code&gt;sudo libinput debug-events&lt;/code&gt;. The catch should also be immediately obvious: ask yourself why this needs to be run with &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;&lt;h3&gt;Wayland doesn’t support screenshots/capture!&lt;/h3&gt;&lt;p&gt;The &lt;a href=&quot;https://github.com/wayland-project/wayland/blob/master/protocol/wayland.xml&quot; target=&quot;_blank&quot;&gt;core Wayland protocol&lt;/a&gt; does not define a mechanism for taking screenshots. Here’s another thing it doesn’t define: how to open application windows, like gedit and Firefox. The Wayland protocol is very conservative and general purpose, and is built with use-cases other than desktop systems in mind. To this end it only implements the lowest common denominator, and leaves the rest to protocol extensions. There is a process for defining, implementing, maturing, and standardizing these extensions, though the last part is in need of improvements - which are under discussion.&lt;/p&gt;&lt;p&gt;There are two protocols for the purpose of screenshots and screen recording, which are developed by wlroots and supported by a strong majority of Wayland compositors: &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/master/protocol/wlr-screencopy-unstable-v1.xml&quot; target=&quot;_blank&quot;&gt;screencopy&lt;/a&gt; and &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/master/protocol/wlr-export-dmabuf-unstable-v1.xml&quot; target=&quot;_blank&quot;&gt;dmabuf-export&lt;/a&gt;, respectively for copying pixels (best for screenshots) and exporting DMA buffers (best for real-time video capture).&lt;/p&gt;&lt;p&gt;There are two approaches to this endorsed by different camps in Wayland: these Wayland protocols, and a dbus protocol based on Pipewire. Progress is being made on making these approaches talk to each other via &lt;a href=&quot;https://github.com/emersion/xdg-desktop-portal-wlr&quot; target=&quot;_blank&quot;&gt;xdg-desktop-portal&lt;/a&gt;, which will make just about every client and compositor work together.&lt;/p&gt;&lt;h3&gt;Wayland doesn’t have a secondary clipboard!&lt;/h3&gt;&lt;p&gt;Secondary clipboard support (aka primary selection) was first implemented as &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/master/protocol/gtk-primary-selection.xml&quot; target=&quot;_blank&quot;&gt;gtk-primary-selection&lt;/a&gt; and was recently standardized as &lt;a href=&quot;https://github.com/wayland-project/wayland-protocols/blob/master/unstable/primary-selection/primary-selection-unstable-v1.xml&quot; target=&quot;_blank&quot;&gt;wp-primary-selection&lt;/a&gt;. It is supported by nearly all Wayland compositors and clients.&lt;/p&gt;&lt;h3&gt;Wayland doesn’t support clipboard managers!&lt;/h3&gt;&lt;p&gt;See &lt;a href=&quot;https://github.com/bugaevc/wl-clipboard&quot; target=&quot;_blank&quot;&gt;wl-clipboard&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Wayland isn’t suitable for embedded devices!&lt;/h3&gt;&lt;p&gt;Some people argue that Wayland isn’t supported on embedded devices or require proprietary blobs to work. This is &lt;em&gt;very&lt;/em&gt; untrue. Firstly, Wayland is a protocol: the &lt;em&gt;implementations&lt;/em&gt; are the ones that need support from drivers, and a Wayland implementation could be written for basically any driver. You could implement Wayland by writing Wayland protocol messages on pieces of paper, passing them to your friend in class, and having them draw your window on their notebook with a pencil.&lt;/p&gt;&lt;p&gt;That being said, this is also untrue of the implementations. wlroots, which contains the most popular Wayland rendering backend, implements KMS+DRM+GBM, which is supported by all open source graphics drivers, and uses GLESv2, which is the most broadly supported graphics implementation, including on embedded (which is what the “E” stands for) and most older hardware. For ancient hardware, writing an fbdev backend is totally possible and I’d merge it in wlroots if someone put in the time. Writing a more modern KMS+DRM+GBM implementation for that hardware is equally possible.&lt;/p&gt;&lt;h3&gt;Wayland doesn’t have network transparency!&lt;/h3&gt;&lt;p&gt;This is actually true! But it’s not as bad as it’s made out to be. Here’s why: X11 forwarding works on Wayland.&lt;/p&gt;&lt;p&gt;Wait, what? Yep: all mainstream desktop Wayland compositors have support for &lt;strong&gt;Xwayland&lt;/strong&gt;, which is an implementation of the X11 server which translates X11 to Wayland, for backwards compatibility. X11 forwarding works with it! So if you use X11 forwarding on Xorg today, your workflow will work on Wayland unchanged.&lt;/p&gt;&lt;p&gt;However, Wayland itself is not network transparent. The reason for this is that some protocols rely on file descriptors for transferring information quickly or in bulk. One example is GPU buffers, so that the Wayland compositor can render clients without copying data on the GPU - which improves performance dramatically. However, little about Wayland is inherently network &lt;em&gt;opaque&lt;/em&gt;. Things like sending pixel buffers to the compositor are already abstracted on Wayland and a network-backed implementation could be easily made. The problem is that no one seems to really care: all of the people who want network transparency drank the anti-Wayland kool-aid instead of showing up to put the work in. If you want to implement this, though, we’re here and ready to support you! Drop by the wlroots &lt;a href=&quot;https://webchat.freenode.net/?channels=sway-devel&quot; target=&quot;_blank&quot;&gt;IRC channel&lt;/a&gt; and we’re prepared to help you implement this.&lt;/p&gt;&lt;h3&gt;Wayland doesn’t support remote desktop!&lt;/h3&gt;&lt;p&gt;This one is also true, but work is ongoing. Several of the pieces are in place: screen capture and keyboard simulation are there. If an interested developer wants to add pointer device simulation and tie it all together with librdesktop, that would be a great boon to the Wayland ecosystem. &lt;a href=&quot;https://webchat.freenode.net/?channels=sway-devel&quot; target=&quot;_blank&quot;&gt;We’re waiting to help!&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Wayland requires client side decorations!&lt;/h3&gt;&lt;p&gt;This was actually true for a long time, but there was deep contention in the Wayland ecosystem over this matter. We fought long and hard over this and we now have a protocol for negotiating client- vs server-side decorations, which is now fairly broadly supported, including among some of its opponents. You’re welcome.&lt;/p&gt;&lt;h3&gt;Wayland doesn’t support hotkey daemons!&lt;/h3&gt;&lt;p&gt;This is a feature, not a bug, but you’re free to disagree once you hear the rationale. There are lots of problems with the idea of hotkey daemons as it exists on X. What if there’s a conflict between several clients who want the same hotkey? What if the user wants to pick a different hotkey? On top of this, designing a protocol carefully to avoid keylogging concerns makes it more difficult still.&lt;/p&gt;&lt;p&gt;To this end, I’ve been encouraging client developers who want hotkeys to instead use some kind of IPC mechanism and a control binary. For example, &lt;code&gt;mako&lt;/code&gt;, a notification daemon, allows you to dismiss notifications by running the &lt;code&gt;makoctl dismiss&lt;/code&gt; command. Users are then encouraged to use the compositor’s own keybinding facilities to execute this command. This is more flexible even outside of keybinding - the user might want to execute this behavior through a script, too.&lt;/p&gt;&lt;p&gt;Still, if you &lt;em&gt;really&lt;/em&gt; want hotkeys, you should start the discussion for standardizing a protocol. It will be an uphill battle but I believe that a protocol which addresses everyone’s concerns is theoretically possible. &lt;em&gt;You&lt;/em&gt; have to step up, though: no one working on Wayland today seems to care. We are mostly volunteers working for free in our spare time.&lt;/p&gt;&lt;h3&gt;Wayland doesn’t support Nvidia!&lt;/h3&gt;&lt;p&gt;Actually, Nvidia doesn’t support us. There are three standard APIs which are implemented by all graphics drivers in the Linux kernel: DRM (display resource management), KMS (kernel mode setting), and GBM (generic buffer management). All three are necessary for most Wayland compositors. Only the first two are implemented by the Nvidia proprietary driver. In order to support Nvidia, Wayland compositors need to add code resembling this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;nvidia&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;proprietary&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;driver&lt;/span&gt;) {
    &lt;span class=&quot;comment&quot;&gt;/* several thousand lines of code */&lt;/span&gt;
} &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;/* several thousand lines of code */&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s terrible! On top of that, we cannot debug the proprietary driver, we cannot send fixes upstream, and we cannot read the code to understand its behavior. The mesa code (where much of the important code for many drivers lives) is a frequent object of study among Wayland compositor developers. We cannot do this with the proprietary drivers, and it doesn’t even implement the APIs it needs to. They claim to be working on a replacement for GBM which they hope will satisfy everyone’s concerns, but 52 commits in 3 years with over a year of inactivity isn’t a great sign.&lt;/p&gt;&lt;p&gt;To boot, Nvidia is a bad actor on Linux. Compare the talks at FOSDEM 2018 from the &lt;a href=&quot;https://archive.fosdem.org/2018/schedule/event/nouveau/&quot; target=&quot;_blank&quot;&gt;nouveau developers&lt;/a&gt; (the open source Nvidia driver) and the &lt;a href=&quot;https://archive.fosdem.org/2018/schedule/event/amd_graphics/&quot; target=&quot;_blank&quot;&gt;AMDGPU developers&lt;/a&gt; (the &lt;em&gt;only&lt;/em&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; AMD driver - also open source). The Nouveau developers discuss all of the ways that Nvidia makes their lives difficult, up to and including &lt;em&gt;signed firmwares&lt;/em&gt;. AMDGPU instead talks about the process of upstreaming their driver, discuss their new open source Vulkan driver, and how the community can contribute - and this was presented by paid AMD staff. I met Intel employees at XDC who were working on a continuous integration system wherein Intel offers a massive Intel GPU farm to Mesa developers free-of-charge for working on the open source driver. Nvidia is clearly a force for bad on the Linux scene and for open source in general, and the users who reward this by spending oodles of cash on their graphics cards are not exactly in my good graces.&lt;/p&gt;&lt;p&gt;So in short, people asking for Nvidia proprietary driver support are asking the wrong people to spend hundreds of hours working for free to write and maintain an implementation for &lt;em&gt;one&lt;/em&gt; driver which represents a harmful force on the Linux ecosystem and a headache for developers trying to work with it. With respect, my answer is no.&lt;/p&gt;&lt;h3&gt;Wayland doesn’t support gaming!&lt;/h3&gt;&lt;p&gt;First-person shooters, among other kinds of games, require “locking” the pointer to their window. This requires &lt;a href=&quot;https://github.com/wayland-project/wayland-protocols/blob/master/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml&quot; target=&quot;_blank&quot;&gt;a protocol&lt;/a&gt;, which was standardized in 2015. Adoption has been slower, but it landed in wlroots several months ago and support was added to sway a few weeks ago.&lt;/p&gt;&lt;h3&gt;In conclusion&lt;/h3&gt;&lt;p&gt;At some point, some of these things have been true. Some have never been true. It takes time to replace a 30-year incumbent. To be fair, some of these points are true on GNOME and KDE, but none are inherently problems with the Wayland protocol. wlroots is a dominating force in the Wayland ecosystem and the tide is clearly moving our way.&lt;/p&gt;&lt;p&gt;Another thing I want to note is that Xorg still works. If you find your needs aren’t met by Wayland, just keep using X! We won’t be offended. I’m not trying to force you to use it. Why you heff to be mad?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Wayland-misconceptions-debunked/</link>
        
        <pubDate>Sun, 10 Feb 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Wayland-misconceptions-debunked/</guid>
      </item>
    
      <item>
        
        
          <title>My experiences at FOSDEM 2019</title>
          <description>
            &lt;p&gt;Currently in a plane on my way home from FOSDEM and, as seems to be a recurring pattern when I fly long distances home after attending a conference, a recap is readily flowing from my fingertips. This was my first year at FOSDEM, and I’m glad that I came. I’m already excited for next year! It was also my first year volunteering, which was equally great and another thing I expect to repeat.&lt;/p&gt;&lt;p&gt;My biggest feeling during the event was one of incredible business. My scatterbrained interests throughout the domain of free software came back to haunt me as I struggled to keep up with all of the people I had to meet &amp; thank, all of the sessions I wanted to see, and all of the dinners &amp; outings I wanted to attend. Before all of the fuss, though, I was lucky enough to have a day and a half to myself (and later with &lt;a href=&quot;https://emersion.fr&quot; target=&quot;_blank&quot;&gt;Simon Ser&lt;/a&gt;) to enjoy Brussels with.&lt;/p&gt;&lt;p&gt;The first FOSDEM-related event I found myself was when the Arch Linux developers graciously invited me to their dinner on Friday. I have a long friendship with several Arch developers, but never met any in person. We were speaking in the weeks before FOSDEM about how to save them from their subversion nightmare, and we spoke a little bit about some ideas for fixing this, but mostly we just had a good time and got to know each other better. Later in the week, Jerome finally convinced me to apply to become an Arch Trusted User, and in the coming months I hope to work with them on a nice next-generation system for Arch Linux package maintenance.&lt;/p&gt;&lt;p&gt;The hallway track&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; continued to be the highlight of the event. Later Friday night, I had volunteered to staff the FOSDEM beer event’s late shift, so the inevitability of time and biology led to missing the first half of day one. I ended up wiggling my way into the BSD room and saw a cool talk on NetBSD - long one of my favorites among the BSDs, and learned that the speaker had a cool project which will save me a lot of time when adding NetBSD support to sr.ht. Grabbed his email afterwards and met up with my friends from KDE for lunch. We met up with Daniel Stone as well, and spoke for a while about how we’re finally going to approach unifying and standardizing the Wayland ecosystem. This discussion took place waiting outside the graphics room for the Pipewire talk. Simon has been working on a portal to connect sway’s Wayland protocols with the dbus-based ecosystem Pipewire lives in, and along with KDE’s Roman Glig they had some interesting questions for the presenter.&lt;/p&gt;&lt;p&gt;The second day was quite a bit different. My other role as a volunteer was doing A/V support in the rooms. For this I got a &lt;em&gt;second&lt;/em&gt; shirt, with a different color! I think next year I may try to collect them all. This was interesting and slow work, and basically entailed walking down to the stage crouched down to tweak the mic volume until someone on IRC from the war room said it was better. I did get to observe more exciting crises over IRC from the comfort of my relatively normal room, though, and got to play a bit with the astonishingly sophisticated A/V setup FOSDEM uses. After that I grabbed a light lunch and passed the time by playing Magic: the Gathering with a group we found in the FOSDEM bar. I grabbed some Club Mates - I love them but they’re super difficult to get in the United States - and waited until the highlight of the event: the sr.ht and sway meetups.&lt;/p&gt;&lt;p&gt;Big shoutout to the FOSDEM organizers for entertaining our last-minute requests to have a space to meet users of both groups. The turnout for both rooms was way more than I expected - almost 50! It seemed like every seat was filled. I was also surprised at how distinct the groups where, with only a 5-10% overlap. After making sure everyone got a sticker, there was some really great questions and feedback from the sr.ht crowd. A particularly interesting tangent had me defending the email choice to a skeptic and getting a lot of good feedback and insights from the rest of the room, as well as elaborating on my plans to improve the workflow for those less comfortable with email. There was naturally some discussion about the crappy name and my plans to fix it, and I had the pleasure of demoing the experimental Fedora builds live to someone who was asking when there would be Fedora support. It was also great to meet many of the users and contributors who I’ve been working with online, and made sure to thank them in person - particularly Ivan Habunek, a prolific sr.ht contributor who was part of our roaming sway/sr.ht/Arch Linux/etc clan throughout FOSDEM.&lt;/p&gt;&lt;p&gt;The sway meetup was equally fun, and I thank the attendees for bearing with me while I answered the post-meetup questions and comments from the sr.ht crowd - my fault for scheduling two back-to-back sessions. We started off with a bang by releasing sway 1.0-rc1, then turned to questions and feedback from the crowd. Simon had a lot to say during the sway meetup as well, explaining his work and future plans for the project, and together we also explained our somewhat novel philosophy on project governance that I credit the success of the project to. It’s designed to maximize contributors, and it’s entirely to their credit that the success of sway and wlroots is owed. Speaking of the future of sway and wlroots, I also met Guido, an engineer at Purism who works with wlroots, again after our initial meeting at XDC 2018. This time, Guido brought a gift - a Librem 5 dev board for the wlroots team to use. Thank you! You’ll hear more about our work with this board in the coming months as I use it to improve touch support for sway and send it out on loan to various wlroots project developers.&lt;/p&gt;&lt;p&gt;I had a flight home Sunday evening so we had a hasty and delicious dinner, a quick round of beers, and finally parted ways. An overnight in Dublin and here I am - on the plane home to Philly, with 43% of my battery&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and an estimated 3 hours left in-flight. FOSDEM was great - a huge thanks to the organizers and volunteers! I’m looking forward to next year.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/FOSDEM-recap/</link>
        
        <pubDate>Tue, 05 Feb 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/FOSDEM-recap/</guid>
      </item>
    
      <item>
        
        
          <title>Why I chose Flask to build sr.ht&apos;s mini-services</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt; is a large, production-scale suite of web applications (I call them “mini-services”, as they strike a balance between microservices and monolithic applications) which are built in Python with &lt;a href=&quot;http://flask.pocoo.org/&quot; target=&quot;_blank&quot;&gt;Flask&lt;/a&gt;. David Lord, one of the maintainers of Flask, reached out to me when he heard about sr.ht and saw that it was built with Flask. At his urging, I’d like to share the rationale behind the decision and how it’s turned out in the long run.&lt;/p&gt;&lt;p&gt;I have a long history of writing web applications with Flask, so much so that I think I’ve lost count of them by now - at least 15, if not 20. Flask’s simplicity and flexibility is what keeps bringing me back. Frameworks like Django or Rails are much different: they are the kitchen sink, and then some. I generally don’t need the whole kitchen sink, and if I were given it, I would want to change some details. Flask is nice because it gives you the basics and lets you build what you need on top of it, and you’re never working around a cookie-cutter system which doesn’t cut your cookies in quite the way you need.&lt;/p&gt;&lt;p&gt;In sr.ht’s case in particular, though, I have chosen to extend Flask with &lt;a href=&quot;https://git.sr.ht/~sircmpwn/core.sr.ht&quot; target=&quot;_blank&quot;&gt;a new module&lt;/a&gt; common to all sr.ht projects. After all, each service of sr.ht has a lot in common with the rest. Some of the things that live in this core module are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Shared jinja2 templates and stylesheets&lt;/li&gt;&lt;li&gt;Shared rigging for &lt;a href=&quot;https://www.sqlalchemy.org/&quot; target=&quot;_blank&quot;&gt;SQLAlchemy&lt;/a&gt; (ORM)&lt;/li&gt;&lt;li&gt;Shared rigging for &lt;a href=&quot;https://alembic.sqlalchemy.org/en/latest/&quot; target=&quot;_blank&quot;&gt;Alembic&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/srht/validation.py&quot; target=&quot;_blank&quot;&gt;A little validation module&lt;/a&gt; I’m very proud of&lt;/li&gt;&lt;li&gt;API behavior, webhooks, OAuth, etc, which are consistent throughout sr.ht&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The mini-service-oriented architecture allows sr.ht services to be deployed ala-carte for users who only need a fraction of what we offer. This design requires a lot of custom code to integrate all of the services with each other - for example, all of the services use a single shared config file, which contains both shared config options and service-specific configuration. sr.ht also uses a novel approach to authentication, in which both user logins and API authentication is delegated to an external service, meta.sr.ht, requiring further custom code still. core.sr.ht additionally provides common SQLAlchemy mixins for things like user tables, which have many common properties, but for each service may have service-specific columns as well.&lt;/p&gt;&lt;p&gt;Django provides their own ORM, their own authentication, their own models, and more. In order to meet the design constraints of sr.ht, I’d have spent twice as long ripping out the rest of Django’s bits and fixing anything that broke in the resulting mess. With Flask, these bits were never written for me in the first place, which gives me the freedom to implement this design greenfield. Flask is small and what code it does bring to the table is highly pluggable.&lt;/p&gt;&lt;p&gt;Though it’s well suited to many of my needs, I don’t think Flask is perfect. A few things I dislike about it:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;First-class &lt;a href=&quot;http://jinja.pocoo.org/&quot; target=&quot;_blank&quot;&gt;jinja2&lt;/a&gt; support is probably out of scope.&lt;/li&gt;&lt;li&gt;flask.Flask and flask.Blueprint should be the same thing.&lt;/li&gt;&lt;li&gt;I’m not a fan of Flask’s approach to configuration. I have a better(?) config module that I drag around to all of my projects.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;And to summarize the good:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;It provides a nice no-nonsense interface for requests, responses, and routing.&lt;/li&gt;&lt;li&gt;It has a lot of nice hooks for adding your own middleware.&lt;/li&gt;&lt;li&gt;It doesn’t do much more than that, which means you’re free to choose and compose other tools to make up the difference.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I think that on the whole it’s quite good. There are frameworks which are smaller still - but I think Flask hits a sweet spot. If you’re making a monolithic web app and can live within the on-rails Django experience, you might want to use it. But if you are making smaller apps or need to rig things up in a unique way - something I find myself doing almost every time - Flask is probably for you.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Why-I-built-sr.ht-with-Flask/</link>
        
        <pubDate>Wed, 30 Jan 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Why-I-built-sr.ht-with-Flask/</guid>
      </item>
    
      <item>
        
        
          <title>Why I use old hardware</title>
          <description>
            &lt;p&gt;Recently I was making sure my main laptop is ready for travel&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, which mostly just entails syncing up the latest version of my music collection. This laptop is a Thinkpad X200, which turns 11 years old in July and is my main workstation away from home (though I bring a second monitor and an external keyboard for long trips). This laptop is a great piece of hardware. 100% of the hardware is supported by the upstream Linux kernel, including the usual offenders like WiFi and Bluetooth. Niche operating systems like 9front and Minix work great, too. Even coreboot works! It’s durable, user-serviceable, light, and still looks brand new after all of these years. I love all of these things, but there’s no denying that it’s 11 years behind on performance innovations.&lt;/p&gt;&lt;p&gt;Last year &lt;a href=&quot;https://kde.org&quot; target=&quot;_blank&quot;&gt;KDE&lt;/a&gt; generously &lt;a href=&quot;https://drewdevault.com/2018/04/28/KDE-Sprint-retrospective.html&quot; target=&quot;_blank&quot;&gt;invited me&lt;/a&gt; to and sponsored my travel to their development sprint in Berlin. One of my friends there teased me - in a friendly way - about my laptop, asking why I used such an old system. There was a pensive moment when I answered: “it forces me to empathise with users who can’t use high-end hardware”. I showed him how it could cold boot to a productive &lt;a href=&quot;https://swaywm.org&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt; desktop in &lt;30 seconds, then I installed KDE to compare. It doubled the amount of disk space in use, took almost 10x as long to reach a usable desktop, and had severe rendering issues with my old Intel GPU.&lt;/p&gt;&lt;p&gt;To be clear, KDE is a wonderful piece of software and my first recommendation to most non-technical computer users who ask me for advice on using Linux. But software often grows to use the hardware you give it. Software developers tend to be computer enthusiasts, and use enthusiast-grade hardware. In reality, this high-end hardware isn’t really &lt;em&gt;necessary&lt;/em&gt; for most applications outside of video encoding, machine learning, and a few other domains.&lt;/p&gt;&lt;p&gt;I do have a more powerful workstation at home, but it’s not really anything special. I upgrade it very infrequently. I bought a new mid-range GPU which is able to drive my four displays&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; last year, I’ve added the occasional hard drive as it gets full, and I replaced the case with something lighter weight 3 years ago. Outside of those minor upgrades, I’ve been using the same desktop workstation for 7 years, and intend to use it for much longer. My servers are similarly running on older hardware which is spec’d to their needs (actually, I left a lot of room to grow and &lt;em&gt;still&lt;/em&gt; was able to buy old hardware).&lt;/p&gt;&lt;p&gt;My 11-year-old laptop can compile the Linux kernel from scratch in 20 minutes, and it can play 1080p video in real-time. That’s all I need! Many users cannot afford high-end computer hardware, and most have better things to spend their money on. And you know, I work hard for my money, too - if I can get a computer which can do nearly 5 &lt;em&gt;billion&lt;/em&gt; operations per second for $60, that should be sufficient to solve nearly any problem. No doubt, there are faster laptops out there, many of them with similarly impressive levels of compatibility with my ideals. But why bother?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Why-I-use-old-hardware/</link>
        
        <pubDate>Wed, 23 Jan 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Why-I-use-old-hardware/</guid>
      </item>
    
      <item>
        
        
          <title>I&apos;m going to work full-time on free software</title>
          <description>
            &lt;p&gt;Sorry for posting two articles so close to each other - but this is important! As I’m certain many of you know, I maintain a large collection of free software projects, including &lt;a href=&quot;https://github.com/swaywm/sway&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt;, &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;, &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt;, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc&quot; target=&quot;_blank&quot;&gt;scdoc&lt;/a&gt;, &lt;a href=&quot;https://git.sr.ht/~sircmpwn/aerc2&quot; target=&quot;_blank&quot;&gt;aerc&lt;/a&gt;, and &lt;a href=&quot;https://git.sr.ht/~sircmpwn&quot; target=&quot;_blank&quot;&gt;many&lt;/a&gt;, &lt;a href=&quot;https://github.com/ddevault&quot; target=&quot;_blank&quot;&gt;many&lt;/a&gt; more. I contribute to more still, working on projects like &lt;a href=&quot;https://alpinelinux.org&quot; target=&quot;_blank&quot;&gt;Alpine Linux&lt;/a&gt;, &lt;a href=&quot;https://git.sr.ht/~emersion/mrsh&quot; target=&quot;_blank&quot;&gt;mrsh&lt;/a&gt;, &lt;a href=&quot;https://musl-libc.org&quot; target=&quot;_blank&quot;&gt;musl libc&lt;/a&gt;, and anything else I can. Until now, I’ve been working on these in my spare time, but just under a year ago I wrote “&lt;a href=&quot;https://drewdevault.com/2018/02/24/The-road-to-sustainable-FOSS.html&quot; target=&quot;_blank&quot;&gt;The path to sustainably working on FOSS full-time&lt;/a&gt;” laying out my future plans.  Today I’m proud to tell you that, thanks to everyone’s support, I’ll be working on free software full-time starting in February.&lt;/p&gt;&lt;p&gt;I’m so excited! I owe many people a great deal of thanks. To everyone who has donated to my fosspay, Patreon, and LiberaPay accounts: thank you. To all of the sr.ht users who chose to pay for their account despite it being an alpha: thank you. I also owe a thanks to all of the amazing contributors who give their spare time towards making the projects I maintain even better, without whom my software wouldn’t be anywhere near as useful to anyone.&lt;/p&gt;&lt;p&gt;I don’t want to make grandiose promises right away, but I’m confident that increasing my commitment to open source to this degree is going to have a major impact on my projects. For now, my primary focus is sr.ht: its paid users make up the majority of the funding. Relatedly, I plan to invest more time in Alpine Linux on RISC-V and making RISC-V builds available to the sr.ht community.  Sway and wlroots are in good shape as we quickly approach sway 1.0, and for this reason I want to give a higher priority to my smaller, more neglected projects like aerc for the time being. As I learn more about my bandwidth under these new conditions, I’ll expand my plans accordingly.&lt;/p&gt;&lt;p&gt;I need to clarify that despite choosing to work full-time on these projects, my income is going to be negative for a while. I have enough savings and income now that I feel comfortable making the leap, and I plan on working my ass off before my runway ends to earn the additional subscriptions to sr.ht and donations to fosspay et al that will make this decision sustainable in the long term. If that doesn’t happen before I get near the end of my runway, I’ll have to start looking for different work again. I’m depending on your continued support. If you appreciate my work but haven’t yet, please consider &lt;a href=&quot;https://meta.sr.ht/billing/initial&quot; target=&quot;_blank&quot;&gt;buying a subscription to sr.ht&lt;/a&gt; or &lt;a href=&quot;https://liberapay.com/ddevault/donate&quot; target=&quot;_blank&quot;&gt;donating to my general projects fund&lt;/a&gt;. Thank you!&lt;/p&gt;&lt;p&gt;All said, words cannot describe how excited I am. It’s been my dream for years to work on these projects full-time. Hitting this critical threshold of funding allows me to tremendously accelerate the progress of these projects. If you were impressed by what I built in my spare time, just wait until you see what we can accomplish now!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/YsHI.png&quot;&gt;&lt;/p&gt;&lt;p&gt;From the bottom of my heart, thank you for your support!&lt;/p&gt;&lt;p&gt;P.S: I’ll see you at &lt;a href=&quot;https://fosdem.org/2019/&quot; target=&quot;_blank&quot;&gt;FOSDEM&lt;/a&gt;! Ask me for a sticker.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Im-doing-FOSS-full-time/</link>
        
        <pubDate>Tue, 15 Jan 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Im-doing-FOSS-full-time/</guid>
      </item>
    
      <item>
        
        
          <title>Backups &amp; redundancy at sr.ht</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; is &lt;a href=&quot;https://git.sr.ht/~sircmpwn?search=sr.ht&quot; target=&quot;_blank&quot;&gt;100% open source&lt;/a&gt; and I encourage people to install it on their own infrastructure, especially if they’ll be sending patches upstream. However, I am equally thrilled to host sr.ht for you on the “official” instance, and most users find this useful because the maintenance burden is non-trivial. Today I’ll give you an idea of what your subscription fee pays for. In this first post on ops at sr.ht, I’ll talk about backups and redundancy. In future posts, I’ll talk about security, high availability, automation, and more.&lt;/p&gt;&lt;p&gt;As sr.ht is still in the alpha phase, high availability has been on the backburner. However, data integrity has always been of paramount importance to me. The very earliest versions of sr.ht, from well before it was even trying to be a software forge, made a point to never lose a single byte of user data. Outages are okay - so long as when service is restored, everything is still there. Over time I’m working to make outages a thing of the past, too, but let’s start with backups.&lt;/p&gt;&lt;p&gt;There are several ways that sr.ht stores data:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Important data on the filesystem (e.g. bare git repositories)&lt;/li&gt;&lt;li&gt;Important persistent data in PostgreSQL&lt;/li&gt;&lt;li&gt;Unimportant ephemeral data in Redis (&amp; caches)&lt;/li&gt;&lt;li&gt;Miscellaneous filesystem storage, like the operating system&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Some of this data is important and kept redundant (PostgreSQL, git repos), and others are unimportant and is not redundant. For example, I store a rendered Markdown cache for git.sr.ht in Redis. If the Redis cluster goes &lt;em&gt;poof&lt;/em&gt;, the source Markdown is still available, so I don’t bother backing up Redis. Most services run in a VM and I generally don’t store important data on these - the hosts usually only have one hard drive with no backups and no redundancy. If the host dies, I have to reprovision all of those VMs.&lt;/p&gt;&lt;p&gt;Other data is more important. Consider PostgreSQL, which contains some of the most important data for sr.ht. I have one master PostgreSQL server, a dedicated server in the space I colocate in my home town of Philadelphia. I run sr.ht on this server, but I also use it for a variety of other projects - I maintain many myself, and I volunteer as a sysadmin for more still. This box (named Remilia) has four hard drives configured in a ZRAID (ZFS). I buy these hard drives from a variety of vendors, mostly Western Digital and Seagate, and from different batches - reducing the likelihood that they’ll fail around the same time. ZFS is well-known for it’s excellent design, featureset and for simply keeping your data intact, and I don’t trust any other filesystem with important data. I take ZFS snapshots every 15 minutes and retain them for 30 days. These snapshots are important for correcting the “oh shit, I rm’d something important” mistakes - you can mount them later and see what the filesystem looked like at the time they were taken.&lt;/p&gt;&lt;p&gt;On top of this, the PostgreSQL server is set up with two additional important features: continuous archiving and streaming replication. Continuous archiving has PostgreSQL writing each transaction to log files on disk, which represents a re-playable history of the entire database, and allows you to restore the database to any point in time. This helps with “oh shit, I dropped an important table” mistakes. Streaming replication ships changes to an off-site standby server, in this case set up in my second colocation in San Francisco (the main backup box, which we’ll talk about more shortly). This takes a near real-time backup of the database, and has the advantage of being able to quickly failover to it as the primary database during maintenance and outages (more on this during the upcoming high availability article). Soon I’ll be setting up a second failover server as well, on-site.&lt;/p&gt;&lt;p&gt;So there are multiple layers to this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;ZFS &amp; zraid prevents disk failure from causing data loss&lt;/li&gt;&lt;li&gt;ZFS snapshots allows retrieving filesystem-level data from the past&lt;/li&gt;&lt;li&gt;Continuous archiving allows retrieving database-level data from the past&lt;/li&gt;&lt;li&gt;Streaming replication prevents datacenter existence failure from causing data loss&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Having multiple layers of data redundancy here protects sr.ht from a wide variety of failure modes, and also protects each redundant system from itself - if any of these systems fails, there’s another place to get this data from.&lt;/p&gt;&lt;p&gt;The off-site backup in San Francisco (this box is called Konpaku) has a whopping 52T of storage in two ZFS pools, named “small” (4T) and “large” (48T). The PostgreSQL standby server lives in the small pool, and &lt;a href=&quot;https://www.borgbackup.org/&quot; target=&quot;_blank&quot;&gt;borg backups&lt;/a&gt; live in the large pool. This has the same ZFS snapshotting and retention policy as Remilia, and also has drives sourced from a variety of vendors and batches. Borg is how important filesystem-level data is backed up, for example git repositories on git.sr.ht. Borg is nice enough to compress, encrypt, and deduplicate its backups for us, which I take hourly with a cronjob on the machines which own that data. The retention policy is hourly backups stored for 48 hours, daily backups for 2 weeks, and weekly backups stored indefinitely.&lt;/p&gt;&lt;p&gt;There are two other crucial steps in maintaining a working backup system: monitoring and testing. The old wisdom is “you don’t have backups until you’ve tested them”. The simplest monitoring comes from cron - when I provision a new box, I make sure to set &lt;code&gt;MAILTO&lt;/code&gt;, make sure sendmail works, and set up a deliberately failing cron entry to ensure I hear about it when it breaks. I also set up zfs-zed to email me whenever ZFS encounters issues, which also has a test mode you should use. For testing, I periodically provision private replicas of sr.ht services from backups and make sure that they work as expected. PostgreSQL replication is fairly new to my setup, but my intention is to switch the primary and standby servers on every database upgrade for HA&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; purposes, which conveniently also tests that each standby is up-to-date and still replicating.&lt;/p&gt;&lt;p&gt;To many veteran sysadmins, a lot of this is basic stuff, but it took me a long time to learn how all of this worked and establish a set of best practices for myself. With the rise in popularity of managed ops like AWS and GCP, it seems like ops &amp; sysadmin roles are becoming less common. Some of us still love the sound of a datacenter and the greater level of control you have over your services, and as a bonus my users aren’t worrying about $bigcorp having access to their data.&lt;/p&gt;&lt;p&gt;The next ops thing on my todo list is high availability, which is still in-progress on sr.ht. When it’s done, expect another blog post!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Backups-and-redundancy-at-sr.ht/</link>
        
        <pubDate>Sun, 13 Jan 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Backups-and-redundancy-at-sr.ht/</guid>
      </item>
    
      <item>
        
        
          <title>Patches welcome</title>
          <description>
            &lt;p&gt;Happy new year! This is always a weird “holiday” for me, since all of the fun happened last night. Today is just kind of… I guess a chance for everyone to sober up before work tomorrow? It does tend to invite a sense of reflection and is the ideal time to plan for the year ahead. One of my goals in 2019 is to change more people’s thinking about the open source community and what it means to count among their number.&lt;/p&gt;&lt;p&gt;I think there’s a certain mode of thinking which lends itself to a more productive free software community and a happier free software contributor. Free software is not &lt;em&gt;theirs&lt;/em&gt; - it’s &lt;em&gt;ours&lt;/em&gt;. Linux doesn’t belong to Linus Torvalds. Firefox doesn’t belong to Mozilla, vim doesn’t belong to Bram Moolenaar, and ffmpeg doesn’t belong to Fabrice Bellard. These projects belong to everyone. That includes you! In this way, we reap the benefits of open source, but we also shoulder the responsibilities. I’m not referring to some abstract sense of reponsibility, but the tangible ones, like fixing bugs or developing new features.&lt;/p&gt;&lt;p&gt;One of the great things about this community is how easy it is to release your software under a FOSS license. You have no obligations to the software once it’s released, except the obligations you hold yourself to (i.e. “if this software makes my computer work, and I want to use my computer, I need to keep this software in good working order”). It’s important for users to remember that they’re not entitled to anything other than the rights laid out in the license, too. You’re not entitled to bug fixes or new features - you’re &lt;em&gt;empowered&lt;/em&gt; by free software to make those changes yourself.&lt;/p&gt;&lt;p&gt;Sometimes, when working on sway, someone says something like “oh, it’s a bug in libwayland”. My response is generally along the lines of “I guess you’re writing a libwayland patch then!” The goal hasn’t changed, only the route. It’s no different from being in the weeds and realizing you need to do some refactoring first. If a problem in some FOSS project, be it a bug or a conspicuously missing feature, is in the way of your goals, it’s &lt;em&gt;your problem&lt;/em&gt;.  A friend of mine recently said of a missing feature in a project they have nothing to do with: “adding FreeBSD 12 support is not yet done, but it’s on my todo list.” I thought that perfectly embodied the right way to think about FOSS.&lt;/p&gt;&lt;p&gt;When applying this philosophy, you may occasionally have to deal with an absentee maintainer or a big old pile of legacy spaghetti code. Fork it! Rewrite it! These are tough marbles but they’re the marbles you’ve gotta deal with. It’s not as hard as it looks.&lt;/p&gt;&lt;p&gt;The entire world of free software is your oyster. Nothing is off-limits: if it’s FOSS, you can work on it. Try not to be intimidated by unknown programming languages, unfamiliar codebases, or a lack of time. You’ll pick up the new language sooner than you think&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, all projects are similar enough when you get down to it, and small amounts of work done infrequently adds up over a long enough time period.  FOSS doesn’t have to move quickly, it just has to keep moving. The Dawn spacecraft accelerated at 0.003 cm/s&lt;sup&gt;2&lt;/sup&gt; and made it to &lt;a href=&quot;https://upload.wikimedia.org/wikipedia/commons/a/a1/PIA19547-Ceres-DwarfPlanet-Dawn-RC3-AnimationFrame25-20150504.jpg&quot; target=&quot;_blank&quot;&gt;another world&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Patches-welcome/</link>
        
        <pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Patches-welcome/</guid>
      </item>
    
      <item>
        
        
          <title>Anatomy of a shell</title>
          <description>
            &lt;p&gt;I’ve been contributing where I can to Simon Ser’s &lt;a href=&quot;https://git.sr.ht/~emersion/mrsh&quot; target=&quot;_blank&quot;&gt;mrsh&lt;/a&gt; project, a work-in-progress strictly POSIX shell implementation. I worked on some small mrsh features during my holiday travels and it’s in the forefront of my mind, so I’d like to share some of its design details with you.&lt;/p&gt;&lt;p&gt;There are two main components to a shell: parsing and execution. mrsh uses a simple &lt;a href=&quot;https://en.wikipedia.org/wiki/Recursive_descent_parser&quot; target=&quot;_blank&quot;&gt;recursive descent parser&lt;/a&gt; to generate an AST (Abstract Syntax Tree, or an in-memory model of the structure of the parsed source). This design was chosen to simplify the code and avoid dependencies like flex/bison, and is a good choice given that performance isn’t critical for parsing shell scripts. Here’s an example of the input source and output AST:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;function&quot;&gt;say_hello&lt;/span&gt;() {
	&lt;span class=&quot;constant function&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;string constant&quot;&gt;&amp;quot;hello &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;!&amp;quot;&lt;/span&gt;
}

&lt;span class=&quot;property&quot;&gt;who&lt;/span&gt;=&lt;span class=&quot;embedded&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;embedded constant function&quot;&gt;whoami&lt;/span&gt;&lt;span class=&quot;embedded&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;say_hello&lt;/span&gt; &lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;who&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script is parsed into this AST (this is the output of &lt;code&gt;mrsh -n test.sh&lt;/code&gt;):&lt;/p&gt;&lt;pre&gt;&lt;code&gt;program
program
└─command_list ─ pipeline
  └─function_definition say_hello ─ brace_group
    └─command_list ─ pipeline
      └─simple_command
        ├─name ─ word_string [3:2 → 3:6] echo
        └─argument 1 ─ word_list (quoted)
          ├─word_string [3:8 → 3:14] hello
          ├─word_parameter
          │ └─name 1
          └─word_string [3:16 → 3:17] !
program
program
└─command_list ─ pipeline
  └─simple_command
    └─assignment
      ├─name who
      └─value ─ word_command ─ program
        └─command_list ─ pipeline
          └─simple_command
            └─name ─ word_string [6:7 → 6:13] whoami
program
└─command_list ─ pipeline
  └─simple_command
    ├─name ─ word_string [7:1 → 7:10] say_hello
    └─argument 1 ─ word_list (quoted)
      └─word_parameter
        └─name who
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Most of these names come directly from the &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html&quot; target=&quot;_blank&quot;&gt;POSIX shell specification&lt;/a&gt;. The parser and AST is made available as a standalone public interface of libmrsh, which can be used for a variety of use-cases like syntax-aware text editors, syntax highlighting (see &lt;a href=&quot;https://git.sr.ht/~emersion/mrsh/tree/master/highlight.c&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;highlight.c&lt;/code&gt;&lt;/a&gt;), linters, etc. The most important use-case is, of course, task planning and execution.&lt;/p&gt;&lt;p&gt;Most of these AST nodes becomes a &lt;em&gt;task&lt;/em&gt;. A task defines an implementation of the following interface:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;task_interface&lt;/span&gt; {
	&lt;span class=&quot;comment&quot;&gt;/**
	 * Request a status update from the task. This starts or continues it.
	 * `poll` must return without blocking with the current task&amp;apos;s status:
	 *
	 * - TASK_STATUS_WAIT in case the task is pending
	 * - TASK_STATUS_ERROR in case a fatal error occured
	 * - A positive (or null) code in case the task finished
	 *
	 * `poll` will be called over and over until the task goes out of the
	 * TASK_STATUS_WAIT state. Once the task is no longer in progress, the
	 * returned state is cached and `poll` won&amp;apos;t be called anymore.
	 */&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; (&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;poll&lt;/span&gt;)(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;task&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ctx&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; (&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;destroy&lt;/span&gt;)(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;task&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the time the task will just do its thing. Many tasks will have sub-tasks as well, such as a command list executing a list of commands, or each branch of an if statement, which it can defer to with &lt;code&gt;task_poll&lt;/code&gt;. Many tasks will wait on an external process, in which case it can return TASK_STATUS_WAIT to have the process &lt;code&gt;wait&lt;/code&gt;ed on. Feel free to browse the &lt;a href=&quot;https://git.sr.ht/~emersion/mrsh/tree/master/shell/task&quot; target=&quot;_blank&quot;&gt;full list of tasks&lt;/a&gt; to get an idea.&lt;/p&gt;&lt;p&gt;One concern more specific to POSIX shells is built-in commands. Some commands have to be built-in because they manipulate the shell’s state, such as &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;cd&lt;/code&gt;. Others, like &lt;code&gt;true&lt;/code&gt; &amp; &lt;code&gt;false&lt;/code&gt;, are there for performance reasons, since they’re simple and easily implemented internally. POSIX specifies &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_14&quot; target=&quot;_blank&quot;&gt;a list of special builtins&lt;/a&gt; which are necessary to implement in the shell itself. There’s &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01&quot; target=&quot;_blank&quot;&gt;a second list&lt;/a&gt; that must be present for the shell environment to be considered POSIX compatible (plus some reserved names like &lt;code&gt;local&lt;/code&gt; and &lt;code&gt;pushd&lt;/code&gt; that invoke undefined behavior - mrsh aborts on these).&lt;/p&gt;&lt;p&gt;Here are some links to more interesting parts of the code so you can explore on your own:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~emersion/mrsh/tree/master/shell/redir.c&quot; target=&quot;_blank&quot;&gt;Redirection&lt;/a&gt; &amp; &lt;a href=&quot;https://git.sr.ht/~emersion/mrsh/tree/master/shell/task/pipeline.c&quot; target=&quot;_blank&quot;&gt;pipelines&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~emersion/mrsh/tree/master/shell/task/function_definition.c&quot; target=&quot;_blank&quot;&gt;Function definition&lt;/a&gt; &amp; &lt;a href=&quot;https://git.sr.ht/~emersion/mrsh/tree/master/shell/task/command_function.c&quot; target=&quot;_blank&quot;&gt;execution&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~emersion/mrsh/tree/master/builtin/dot.c&quot; target=&quot;_blank&quot;&gt;The . builtin&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git.sr.ht/~emersion/mrsh/tree/master/main.c&quot; target=&quot;_blank&quot;&gt;main.c and the REPL&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I might write more articles in the future diving into specific concepts, feel free to shoot me an email if you have suggestions. Shoutout to Simon for building such a cool project! I’m looking forward to contributing more until we have a really nice strictly POSIX shell.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Anatomy-of-a-shell/</link>
        
        <pubDate>Fri, 28 Dec 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Anatomy-of-a-shell/</guid>
      </item>
    
      <item>
        
        
          <title>Porting Alpine Linux to RISC-V</title>
          <description>
            &lt;p&gt;I recently received my &lt;a href=&quot;https://www.sifive.com/boards/hifive-unleashed&quot; target=&quot;_blank&quot;&gt;HiFive Unleashed&lt;/a&gt;, after several excruciating months of waiting, and it’s incredibly cool. For those unaware, the HiFive Unleashed is the first consumer-facing Linux-capable &lt;a href=&quot;https://en.wikipedia.org/wiki/RISC-V&quot; target=&quot;_blank&quot;&gt;RISC-V&lt;/a&gt; hardware. For anyone who’s still lost, RISC-V is an &lt;a href=&quot;https://github.com/riscv&quot; target=&quot;_blank&quot;&gt;open&lt;/a&gt;, royalty-free &lt;a href=&quot;https://en.wikipedia.org/wiki/Instruction_set_architecture&quot; target=&quot;_blank&quot;&gt;instruction set architecture&lt;/a&gt;, and the HiFive is an &lt;a href=&quot;https://github.com/sifive&quot; target=&quot;_blank&quot;&gt;open&lt;/a&gt; CPU implementing it. And here it is on my dining room table:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/JMao.jpg&quot;&gt;&lt;/p&gt;&lt;p&gt;This board is &lt;em&gt;cool&lt;/em&gt;. I’m working on making this hardware available to &lt;a href=&quot;https://meta.sr.ht&quot; target=&quot;_blank&quot;&gt;builds.sr.ht&lt;/a&gt; users in the next few months, where I intend to use it to automate the remainder of the Alpine Linux port and make it available to any other operating systems (including non-Linux) and userspace software which are interested in working on a RISC-V port. I’m fairly certain that this will be the first time hardware-backed RISC-V cycles are being made available to the public.&lt;/p&gt;&lt;p&gt;There are two phases to porting an operating system to a new architecture: bootstrapping and, uh, porting. For lack of a better term. As part of bootstrapping, you need to obtain a cross-compiler, port libc, and cross-compile the basics. Bootstrapping ends once the system is &lt;em&gt;self-hosting&lt;/em&gt;: able to compile itself. The “porting” process involves compiling all of the packages available for your operating system, which can take a long time and is generally automated.&lt;/p&gt;&lt;p&gt;The first order of business is the cross-compiler. RISC-V support landed in binutils 2.28 and gcc 7.1 several releases ago, so no need to worry about adding a RISC-V target to our compiler. Building both with &lt;code&gt;--target=riscv64-linux-musl&lt;/code&gt; is sufficient to complete this step. The other major piece is the C standard library, or libc. Unlike the C compiler, this step required some extra effort on my part - the RISC-V port of musl libc, which Alpine Linux is based on, is a work-in-progress and has not yet been upstreamed.&lt;/p&gt;&lt;p&gt;There does exist &lt;a href=&quot;https://github.com/riscv/riscv-musl&quot; target=&quot;_blank&quot;&gt;a patch&lt;/a&gt; for RISC-V support, though it had never been tested at a scale like this. Accordingly, I ran into several bugs, for which I wrote several patches (&lt;a href=&quot;https://github.com/riscv/riscv-musl/pull/2&quot; target=&quot;_blank&quot;&gt;1&lt;/a&gt; &lt;a href=&quot;https://github.com/riscv/riscv-musl/pull/3&quot; target=&quot;_blank&quot;&gt;2&lt;/a&gt; &lt;a href=&quot;https://github.com/riscv/riscv-musl/pull/4&quot; target=&quot;_blank&quot;&gt;3&lt;/a&gt;). Having a working distro based on the RISC-V port makes a much more compelling argument for the maturity of the port, and for its inclusion upstream, so I’m happy to have caught these issues. Until then, I added the port and my patches to the Alpine Linux musl package manually.&lt;/p&gt;&lt;p&gt;A C compiler and libc implementation open the floodgates to porting a huge volume of software to your platform. The next step is to identify and port the essential packages for a self-hosting system.  For this, Alpine has a great &lt;a href=&quot;https://git.alpinelinux.org/cgit/aports/tree/scripts/bootstrap.sh&quot; target=&quot;_blank&quot;&gt;bootstrapping script&lt;/a&gt; which handles preparing the cross-compiler and building the base system. Many (if not most) of these packages required patching, tweaks, and manual intervention - this isn’t a turnkey solution - but it is an incredibly useful tool. The most important packages at this step are the native toolchain&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, the package manager itself, and various other useful things like tar, patch, openssl, and so on.&lt;/p&gt;&lt;p&gt;Once the essential packages are built and the system can compile itself, the long porting process begins. It’s generally wise to drop the cross-compiler here and start doing native builds, if your hardware is fast enough. This is a tradeoff, because the RISC-V system is somewhat slower than my x86_64 bootstrap machine - but many packages require lots of manual tweaks and patching to get cross-compiling working. The time saved by not worrying about this makes up for the slower build times&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;There are thousands of packages, so the next step for me (and anyone else working on a port) is to automate the remainder of the process. For me, an intermediate step is integrating this with builds.sr.ht to organize my own work and to make cycles available to other people interested in RISC-V. Not all packages are going to be ported for free - but many will! Once you unlock the programming languages - C, Python, Perl, Ruby&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, etc - most open source software is pretty portable across architectures. One of my core goals with sr.ht is to encourage portable software to proliferate!&lt;/p&gt;&lt;p&gt;If any readers have their own RISC-V hardware, or want to try it with qemu, I have a RISC-V Alpine Linux repository here&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. Something like this will install it to /mnt:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant function&quot;&gt;apk&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;add&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-X&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;https://mirror.sr.ht/alpine/main/&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;--allow-untrusted&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;--arch=riscv64&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;--root=/mnt&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;alpine-base&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;alpine-sdk&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;vim&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;chrony&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;/bin/busybox --install&lt;/code&gt; and &lt;code&gt;apk fix&lt;/code&gt; on first boot. This is still a work in progress, so configuring the rest is an exercise left to the reader until I can clean up the process and make a nice install script. Good luck!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Closing note: big thanks to the help from the community in #riscv on Freenode, and to the hard work of the Debian and Fedora teams paving a lot of the way and getting patches out there for lots of software! I still got to have all the fun working on musl so I wasn’t entirely on the beaten path :)&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Porting-Alpine-Linux-to-RISC-V/</link>
        
        <pubDate>Thu, 20 Dec 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Porting-Alpine-Linux-to-RISC-V/</guid>
      </item>
    
      <item>
        
        
          <title>How to abandon a FLOSS project</title>
          <description>
            &lt;p&gt;It’s no secret that maintaining free and open source software is often a burdensome and thankless job. I empathise with maintainers who lost interest in a project, became demotivated by the endless demands of users, or are no longer blessed with enough free time. Whatever the reason, FLOSS work is volunteer work, and you’re free to stop volunteering at any time.&lt;/p&gt;&lt;p&gt;In my opinion, there are two good ways to abandon a project: the &lt;em&gt;fork it&lt;/em&gt; option and the &lt;em&gt;hand-off&lt;/em&gt; option. The former is faster and easier, and you can pick this if you want to wash your hands of the project ASAP, but has a larger effect on the community. The latter is not always possible, requires more work on your part, and takes longer, but it has a minimal impact on the community.&lt;/p&gt;&lt;p&gt;Let’s talk about the easy way first. Start by adding a notice to your README that your software is now unmaintained. If you have the patience, give a few weeks notice before you really stop paying attention to it. Inform interested parties that they should consider forking the software and maintaining it themselves under another name. Once a fork gains traction, update the README again to direct would-be users to the fork. If no one forks it, you could consider directing users to similar alternatives to your software.&lt;/p&gt;&lt;p&gt;This approach allows you to quickly absolve yourself of responsibility. Your software is no worse than it was yesterday, which allows users a grace period to collect themselves and start up a fork. If you revisit your work later, you can also become a contributor to the fork yourself, which removes the stress of being a maintainer while still providing value to the project. Or, you can just wash your hands of it entirely and move on to bigger and better things. This “fork it” approach is safer than giving control of your project to passerby, because it requires your users to acknowledge the transfer of power, instead of being surprised by a new maintainer in a trusted package.&lt;/p&gt;&lt;p&gt;The “fork it” approach is well suited when the maintainer wants out ASAP, or for smaller projects with little activity. But, for active projects with a patient maintainer, the hand-off approach is less disruptive. Start talking with some of your major contributors about &lt;a href=&quot;https://drewdevault.com/blog/How-I-maintain-FOSS-projects/&quot;&gt;increasing their involvement&lt;/a&gt; in the administrative side of the projects. Mentor them on doing code reviews, ticket triage, sysadmin stuff, marketing - all the stuff you have to do - and gradually share these responsibilities with them.  These people eventually become productive co-maintainers, and once established you can step away from the project with little fanfare.&lt;/p&gt;&lt;p&gt;Taking this approach can also help you find healthier ways to be involved in your own project. This can allow you to focus on the work you enjoy and spend less time on the work you don’t enjoy, which might even restore your enthusiasm for the project outright! This is also a good idea even if you aren’t planning on stepping down - it encourages your contributors to take personal stake in the project, which makes them more productive and engaged. This also makes your community more resilient to &lt;a href=&quot;https://tvtropes.org/pmwiki/pmwiki.php/Main/AuthorExistenceFailure&quot; target=&quot;_blank&quot;&gt;author existence failure&lt;/a&gt;, so that when circumstance forces you to step down the project continues to be healthy.&lt;/p&gt;&lt;p&gt;It’s important to always be happy in your work, and especially in your volunteer work. If it’s not working, then change it. For me, this happens in different ways. I’ve abandoned projects outright and sent users off to make their own fork before.  I’ve also handed projects over to their major contributors. In some projects I’ve appointed new maintainers and scaled back my role to a mere contributor, and in other projects I’ve moved towards roles in marketing, outreach, management, and stepped away from development. There’s no shame in any of these changes - you still deserve pride in your accomplishments, and seeking constructive solutions to burnout would do your community a great service.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-abandon-a-FLOSS-project/</link>
        
        <pubDate>Tue, 04 Dec 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-abandon-a-FLOSS-project/</guid>
      </item>
    
      <item>
        
        
          <title>sr.ht, the hacker&apos;s forge, now open for public alpha</title>
          <description>
            &lt;p&gt;I’m happy to announce today that I’m opening &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt; (pronounced “sir hat”, or any other way you want) to the general public for the remainder of the alpha period. Though it’s missing some of the features which will be available when it’s completed, sr.ht today represents a very capable software forge which is already serving the needs of many projects in the free &amp; open source software community. If you’re familiar with the project and ready to register your account, you can head straight to &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;the sign up page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For those who are new, let me explain what makes sr.ht special. It provides many of the trimmings you’re used to from sites like GitHub, Gitlab, BitBucket, and so on, including git repository hosting, bug tracking software, CI, wikis, and so on. However, the sr.ht model is different from these projects - where many forges attempt to replicate GitHub’s success with a thinly veiled clone of the GitHub UI and workflow, sr.ht is fundamentally different in its approach.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The sr.ht platform excites me more than any project in recent memory. It’s a fresh concept, not a Github wannabe like Gitlab. I always thought that if something is going to replace Github it would have to be a paradigm change, and I think that’s what we’re seeing here. Drew’s project blends the wisdom of the kernel hackers with a tasteful web interface.&lt;/p&gt;&lt;/blockquote&gt;&lt;div style=&quot;margin-top: -1rem; margin-bottom: 1rem&quot;&gt;&lt;small&gt;&amp;mdash;&lt;a href=&quot;https://lobste.rs/s/h1udkf/git_is_already_federated_decentralized#c_smnkic&quot;&gt;begriffs on lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;The 500 foot view is that sr.ht is a &lt;a href=&quot;https://git.sr.ht/~sircmpwn/?search=sr.ht&quot; target=&quot;_blank&quot;&gt;100% free and open source&lt;/a&gt; software forge, with a hosted version of the services running &lt;em&gt;at&lt;/em&gt; &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt; for your convenience. Unlike GitHub, which is almost entirely closed source, and Gitlab, which is mostly open source but with a proprietary premium offering, all of sr.ht is completely open source, with a copyleft license&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. You’re welcome to install it on your own hardware, and &lt;a href=&quot;https://man.sr.ht/installation.md&quot; target=&quot;_blank&quot;&gt;detailed instructions&lt;/a&gt; are available for those who want to do so. You can also send patches upstream, which are then integrated into the hosted version.&lt;/p&gt;&lt;p&gt;sr.ht is special because it’s extremely modular and flexible, designed with interoperability with the rest of the ecosystem in mind. On top of that, sr.ht is one of the most lightweight websites on the internet, with the average page weighing less than 10 KiB, with &lt;strong&gt;no tracking&lt;/strong&gt; and &lt;strong&gt;no JavaScript&lt;/strong&gt;. Each component - git hosting, continuous integration, etc - is a standalone piece of software that integrates deeply with the rest of sr.ht &lt;em&gt;and&lt;/em&gt; with the rest of the ecosystem outside of sr.ht. For example, you can use builds.sr.ht to compile your GitHub pull requests, or you can keep your repos on git.sr.ht and host everything in one place. Unlike GitHub, which favors its own in-house pull request workflow&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, sr.ht embraces and improves upon the email-based workflow favored by git itself, along with many of the more hacker-oriented projects around the net. I’ve put a lot of work into making this powerful workflow more &lt;a href=&quot;https://man.sr.ht/git.sr.ht/send-email.md&quot; target=&quot;_blank&quot;&gt;accessible and comprehensible&lt;/a&gt; to the average hacker.&lt;/p&gt;&lt;p&gt;The flagship product from sr.ht is its continuous integration platform, builds.sr.ht, which is easily the most capable continuous integration system available today. It’s so powerful that I’ve been working with multiple Linux distributions on bringing them onboard because it’s the only platform which can scale to the automation needs of an entire Linux distribution. It’s so powerful that I’ve been working with maintainers of &lt;em&gt;non-Linux&lt;/em&gt; operating systems, from BSD to even Hurd, because it’s the only platform which can even consider supporting their needs. Smaller users are loving it, too, many of whom are jumping ship from Travis and Jenkins in favor of the simplicity and power of builds.sr.ht.&lt;/p&gt;&lt;p&gt;On builds.sr.ht, simple YAML-based &lt;a href=&quot;https://man.sr.ht/builds.sr.ht/#build-manifests&quot; target=&quot;_blank&quot;&gt;build manifests&lt;/a&gt;, similar to those you see on other platforms, are used to describe your builds. You can submit these through the web, the API, or various integrations.  Within seconds, a virtual machine is booted with KVM, your build environment is sent to it, and your scripts start running. A diverse set of base images are supported on a variety of architectures, soon to include the first hardware-backed RISC-V cycles available to the general public. builds.sr.ht is used to automate everything from the deployment of this Jekyll-based blog, testing GitHub pull requests for &lt;a href=&quot;https://swaywm.org&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt;, building and testing packages for &lt;a href=&quot;https://postmarketos.org/&quot; target=&quot;_blank&quot;&gt;postmarketOS&lt;/a&gt;, and deploying complex applications like builds.sr.ht itself. Our base images &lt;a href=&quot;https://builds.sr.ht/~sircmpwn/alpine/edge&quot; target=&quot;_blank&quot;&gt;build, test, and deploy themselves&lt;/a&gt; every day.&lt;/p&gt;&lt;p&gt;The lists.sr.ht service is another important part of sr.ht, and a large part of how sr.ht embraces the model used by major projects like Linux, Postgresql, git itself, and many more. lists.sr.ht finally modernizes mailing lists, with a powerful and elegant web interface for hacking on and talking about your projects. Take a look at the &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/sr.ht-dev&quot; target=&quot;_blank&quot;&gt;sr.ht-dev&lt;/a&gt; list to see patches developed for sr.ht itself. Another good read is the &lt;a href=&quot;https://lists.sr.ht/~emersion/mrsh-dev&quot; target=&quot;_blank&quot;&gt;mrsh-dev&lt;/a&gt; list, used for development on the &lt;a href=&quot;https://git.sr.ht/~emersion/mrsh&quot; target=&quot;_blank&quot;&gt;mrsh&lt;/a&gt; project, or my own &lt;a href=&quot;https://lists.sr.ht/~sircmpwn/public-inbox&quot; target=&quot;_blank&quot;&gt;public inbox&lt;/a&gt;, where I take comments for this blog and grab-bag discussions for my smaller projects.&lt;/p&gt;&lt;p&gt;I’ve just scratched the surface, and there’s much more for you to discover. You could look at my &lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc&quot; target=&quot;_blank&quot;&gt;scdoc&lt;/a&gt; project to get an idea of how the git browser looks and feels. You could &lt;a href=&quot;https://todo.sr.ht/~sircmpwn&quot; target=&quot;_blank&quot;&gt;browse tickets on my todo.sr.ht profile&lt;/a&gt; to get a feel for the bug tracking software. Or you could check out the &lt;a href=&quot;https://man.sr.ht&quot; target=&quot;_blank&quot;&gt;detailed manual&lt;/a&gt; on sr.ht’s git-powered wiki service. You could also just &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sign up for an account&lt;/a&gt;!&lt;/p&gt;&lt;p&gt;sr.ht isn’t complete, but it’s maturing fast and I think you’ll love it. Give it a try, and I’m only &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;an email away&lt;/a&gt; to receive your feedback.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/sr.ht-general-availability/</link>
        
        <pubDate>Thu, 15 Nov 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/sr.ht-general-availability/</guid>
      </item>
    
      <item>
        
        
          <title>It&apos;s not okay to pretend your software is open source</title>
          <description>
            &lt;p&gt;Unfortunately, I find myself writing about the Commons Clause again. For those not in the know, the Commons Clause is an addendum designed to be added to free software licenses. The restrictions it imposes (you cannot sell the software) makes the resulting franken-license nonfree. I’m not going to link to the project which brought this subject back into the discussion - they don’t deserve the referral - but the continued proliferation of software using the Commons Clause gives me reason to speak out against it some more.&lt;/p&gt;&lt;p&gt;One of my largest complaints with the Commons Clause is that it hijacks language used by open source projects to proliferate nonfree software, and encourages software using it to do the same. Instead of being a new software license, it tries to stick itself onto other respected licences - often the Apache 2.0 license. The name, “Commons Clause”, is also disingenuous, hijacking language used by respected entities like Creative Commons. In truth, the Commons Clause serves to remove software from the commons&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Combining these problems gives you language like “Apache+Commons Clause”, which is easily confused with [Apache Commons][apache-commons].&lt;/p&gt;&lt;p&gt;Projects using the Commons Clause have also been known to describe their license as “permissive” or “open”, some even calling their software “open source”. This is dishonest. FOSS refers to “free and open source software”. The former, free software, is defined by the &lt;a href=&quot;https://www.gnu.org/philosophy/free-sw.en.html&quot; target=&quot;_blank&quot;&gt;free software definition&lt;/a&gt;, published by &lt;a href=&quot;https://gnu.org&quot; target=&quot;_blank&quot;&gt;GNU&lt;/a&gt;. The latter, open source software, is defined by the &lt;a href=&quot;https://opensource.org/osd&quot; target=&quot;_blank&quot;&gt;open source definition&lt;/a&gt;, published by the &lt;a href=&quot;https://opensource.org&quot; target=&quot;_blank&quot;&gt;OSI&lt;/a&gt;. Their definitions are very similar, and nearly all FOSS licenses qualify under both definitions. These are unambiguous, basic criteria which protects developers, contributors, and users of free and open source software. These definitions are so basic, important and well-respected that dismissing them is akin to dismissing climate change.&lt;/p&gt;&lt;p&gt;Claiming your software is open source, permissively licensed, free software, etc, when you use the Commons Clause, is &lt;em&gt;lying&lt;/em&gt;. These lies are pervasive among users of the Commons Clause. The page listing &lt;a href=&quot;https://redis.io/modules&quot; target=&quot;_blank&quot;&gt;Redis Modules&lt;/a&gt;, for example, states that only software under an OSI-approved license is listed. Six of the modules there are using nonfree licenses, and antirez seems content to &lt;a href=&quot;https://github.com/antirez/redis-doc/pull/984&quot; target=&quot;_blank&quot;&gt;ignore the problem&lt;/a&gt; until &lt;a href=&quot;https://github.com/RedisLabsModules/RediSearch/issues/518&quot; target=&quot;_blank&quot;&gt;we forget about it&lt;/a&gt;. They’re in for a long wait - we’re not going to forget about &lt;strong&gt;shady, dishonest, and unethical companies like Redis Labs&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;I don’t use nonfree software&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, but I’m not going to sit here and tell you not to make nonfree software. You have every right to license your work in any way you choose. However, if you choose not to use a FOSS license, you need to own up to it. Don’t pretend that your software is something it’s not. There are many benefits to being a member of the free software community, but you are not entitled to them if your software isn’t. This behavior has to stop.&lt;/p&gt;&lt;p&gt;Finally, I have some praise to offer. &lt;a href=&quot;https://dgraph.io/&quot; target=&quot;_blank&quot;&gt;Dgraph&lt;/a&gt; was briefly licensed under Apache plus the Commons Clause, and had the sort of misleading and false information this article decries on their marketing website, docs, and so on. However, they’ve rolled it back, and Dgraph is now using the Apache 2.0 license with no modifications.  Thank you!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Its-not-okay-to-pretend-youre-open-source/</link>
        
        <pubDate>Tue, 30 Oct 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Its-not-okay-to-pretend-youre-open-source/</guid>
      </item>
    
      <item>
        
        
          <title>How does virtual memory work?</title>
          <description>
            &lt;p&gt;Virtual memory is an essential part of your computer, and has been for several decades. In my &lt;a href=&quot;https://drewdevault.com/blog/Understanding-pointers/&quot;&gt;earlier article on pointers&lt;/a&gt;, I compared memory to a giant array of octets (bytes), and explained some of the abstractions we make on top of that. In actual fact, memory is more complicated than a flat array of bytes, and in this article I’ll explain how.&lt;/p&gt;&lt;p&gt;An astute reader of my earlier article may have considered that pointers on, say, an x86_64 system, are 64 bits long&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. With this, we can address up to 18,446,744,073,709,551,616 bytes (16 exbibytes&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;) of memory. I only have 16 GiB of RAM on this computer, so what gives? What’s the rest of the address space for? The answer: all kinds of things! Only a small subset of your address space is mapped to physical RAM. A system on your computer called the MMU, or Memory Management Unit, is responsible for managing the abstraction that enables this and other uses of your address space. This abstraction is called virtual memory.&lt;/p&gt;&lt;p&gt;The kernel interacts directly with the MMU, and provides syscalls like [mmap(2)][mmap] for userspace programs to do the same. Virtual memory is typically allocated a page at a time, and given a purpose on allocation, along with various flags (documented on the mmap page). When you call &lt;code&gt;malloc&lt;/code&gt;, libc uses the mmap syscall to allocate pages of heap, then assigns a subset of that to the memory you asked for. However, since many programs can run concurrently on your system and may request pages of RAM at any time, your physical RAM can get fragmented.  Each time the kernel hits a context switch&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, it swaps out the page table for the next process.&lt;/p&gt;&lt;p&gt;This is used in this way to give each process its own clean address space and to provide memory isolation between processes, preventing them from accessing each other’s memory. Sometimes, however, in the case of shared memory, the same physical memory is deliberately shared with multiple processes.  Many pages can also be any combination readable, writable, or executable - the latter meaning that you could jump to it and execute it as native code.  Your compiled program is a file, after all - mmap some executable pages, load it into memory, jump to it, and huzzah: you’re running your program&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. This is how JITs, dynamic recompiling emulators, etc, do their job. A common way to reduce risk here, popular on *BSD, is enforcing W^X (writable XOR executable), so that a page can be either writable or executable, but never both.&lt;/p&gt;&lt;p&gt;Sometimes all of the memory you think you have isn’t actually there, too. If you blow your RAM budget across your whole system, swap gets involved. This is when pages of RAM are “swapped” to disk - as soon as your program tries to access it again, a page fault occurs, transferring control to the kernel. The kernel restores from swap, damning some other poor process to the fate, and returns control to your program.&lt;/p&gt;&lt;p&gt;Another very common use for virtual memory is for memory mapped I/O. This can be, for example, mapping a file to memory so you can efficiently read and write to disk. You can map other sorts of hardware, too, such as video memory. On 8086 (which is what your computer probably pretends to be when it initially boots&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;), a simple 96x64 cell text buffer is available at address &lt;code&gt;0xB8000&lt;/code&gt;. On my TI-Nspire CX calculator, I can read the current time from the real-time clock at &lt;code&gt;0x90090000&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;In summary, MMUs arrived almost immediately on the computing scene, and have become increasingly sophisticated ever since. Virtual memory is a powerful tool which grants userspace programmers elegant, convenient, and efficient access to the underlying hardware.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-does-virtual-memory-work/</link>
        
        <pubDate>Mon, 29 Oct 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-does-virtual-memory-work/</guid>
      </item>
    
      <item>
        
        
          <title>Sway 1.0-beta.1 release highlights</title>
          <description>
            &lt;p&gt;1,173 days ago, I wrote sway’s &lt;a href=&quot;https://github.com/swaywm/sway/commit/6a33e1e3cddac31b762e4376e29c03ccf8f92107&quot; target=&quot;_blank&quot;&gt;initial commit&lt;/a&gt;, and 8,269 commits followed&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, written by hundreds of contributors. What started as a side project became the most fully featured and stable Wayland desktop available, and drove the development of what has become the dominant solution for building Wayland compositors - &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;, now the basis of 10 Wayland compositors.&lt;/p&gt;&lt;p&gt;Sway 1.0-beta.1 was just released and is 100% compatible with the &lt;a href=&quot;https://i3wm.org/&quot; target=&quot;_blank&quot;&gt;i3 X11 window manager&lt;/a&gt;. It’s faster, prettier, sips your battery, and supports &lt;a href=&quot;https://wayland.freedesktop.org/&quot; target=&quot;_blank&quot;&gt;Wayland&lt;/a&gt; clients. When we started, I honestly didn’t think we’d get here. When I decided we’d rewrite our internals and build wlroots over a year ago, I didn’t think we’d get here. It’s only thanks to an amazing team of talented contributors that we did. So what can users expect from this release? The difference between sway 0.15 and sway 1.0 is like night and day. The annoying bugs which plauged sway 0.15 are gone, and in their place is a rock solid Wayland compositor with loads of features you’ve been asking after for years. The &lt;a href=&quot;https://github.com/swaywm/sway/releases/tag/1.0-beta.1&quot; target=&quot;_blank&quot;&gt;official release notes&lt;/a&gt; are a bit thick, so let me give you a guided tour.&lt;/p&gt;&lt;h2&gt;New output features&lt;/h2&gt;&lt;p&gt;Outputs, or displays, grew a lot of cool features in sway 1.0. As a reminder, you can get the names of your outputs for use in your config file by using &lt;code&gt;swaymsg -t get_outputs&lt;/code&gt;. What can you do with them?&lt;/p&gt;&lt;p&gt;To rotate your display 90 degrees, use:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;output DP-1 transform 90
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To enable our improved HiDPI support&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, use:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;output DP-1 scale 2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or to enable fractional scaling (see man page for warnings about this):&lt;/p&gt;&lt;pre&gt;&lt;code&gt;output DP-1 scale 1.5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also now run sway on multiple GPUs. It will pick a primary GPU automatically, but you can override this by specifying a list of card names at startup with &lt;code&gt;WLR_DRM_DEVICES=card0:card1:...&lt;/code&gt;. The first one will do all of the rendering and any displays connected to subsequent cards will have their buffers copied over.&lt;/p&gt;&lt;p&gt;Other cool features include support for daisy-chained DisplayPort configurations and improved Redshift support. Also, the long annoying single-output limitation of wlc is behind us: you can now drag windows between outputs with the mouse.&lt;/p&gt;&lt;p&gt;See &lt;code&gt;man 5 sway-output&lt;/code&gt; for more details on configuring these features.&lt;/p&gt;&lt;h2&gt;New input features&lt;/h2&gt;&lt;p&gt;Input devices have also matured a lot. You can get a list of their identifiers with &lt;code&gt;swaymsg -t get_inputs&lt;/code&gt;. One oft requested feature was a better way of configuring your keyboard layout, which you can now do in your config file:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;input &amp;quot;9456:320:Metadot_-_Das_Keyboard_Das_Keyboard&amp;quot; {
    xkb_options caps:escape
    xkb_numlock enabled
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We also now support drawing tablets, which you can bind to a specific output:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;input &amp;quot;1386:827:Wacom_Intuos_S_2_Pen&amp;quot; {
    map_to_output DP-3
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also now do crazy stuff like having multiple mice with multiple cursors, and linking keyboards, mice, drawing tablets, and touchscreens to each other arbitrarily. You can now have your dvorak keyboard for normal use and a second qwerty keyboard for when your coworker comes over for a pair programming session. You can even give your coworker the ability to focus and type into &lt;em&gt;separate&lt;/em&gt; windows from what you’re working on.&lt;/p&gt;&lt;h2&gt;Third-party panels, lockscreens, and more&lt;/h2&gt;&lt;p&gt;Our new layer-shell protcol is starting to take hold in the community, and enables the use of even more third-party software on sway. One of our main commitments to you for sway 1.0 and wlroots is to break the boundaries between Wayland compositors and encourange standard interopable protocols - and we’ve done so. Here are some interesting third-party layer-shell clients in the wild:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Alexays/Waybar&quot; target=&quot;_blank&quot;&gt;Waybar&lt;/a&gt;, a new panel&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/emersion/mako&quot; target=&quot;_blank&quot;&gt;mako&lt;/a&gt;, a notification daemon&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://source.puri.sm/Librem5/virtboard&quot; target=&quot;_blank&quot;&gt;virtboard&lt;/a&gt;, an on-screen keyboard&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/emersion/slurp&quot; target=&quot;_blank&quot;&gt;slurp&lt;/a&gt;, a tool to interactively select a region of the screen&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://source.puri.sm/Librem5/phosh&quot; target=&quot;_blank&quot;&gt;Phosh&lt;/a&gt;, the &lt;a href=&quot;https://puri.sm/&quot; target=&quot;_blank&quot;&gt;Purism&lt;/a&gt; team’s shell for their &lt;a href=&quot;https://puri.sm/shop/librem-5/&quot; target=&quot;_blank&quot;&gt;Librem 5&lt;/a&gt; phone&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We also added two new protocols for capturing your screen: screencopy and dmabuf-export, respectively these are useful for screenshots and real-time screen capture, for example to live stream on Twitch. Some third-party software exists for these, too:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/emersion/grim&quot; target=&quot;_blank&quot;&gt;grim&lt;/a&gt;, for taking screenshots&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/atomnuker/wlstream&quot; target=&quot;_blank&quot;&gt;wlstream&lt;/a&gt;, for recording video&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;DPMS, auto-locking, and idle management&lt;/h2&gt;&lt;p&gt;Our new &lt;code&gt;swayidle&lt;/code&gt; tool adds support for all of these features, and even works on other Wayland compositors. To configure it, start by running the daemon in your sway config file:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;exec swayidle \
    timeout 300 &amp;apos;swaylock -c 000000&amp;apos; \
    timeout 600 &amp;apos;swaymsg &amp;quot;output * dpms off&amp;quot;&amp;apos; \
       resume &amp;apos;swaymsg &amp;quot;output * dpms on&amp;quot;&amp;apos; \
    before-sleep &amp;apos;swaylock -c 000000&amp;apos;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This example will, after 300 seconds of inactivity, lock your screen. Then after 600 seconds, it will turn off all of your outputs (and turn them back on when you wiggle the mouse). This configuration also locks your screen before your system goes to sleep. None of this will happen if you’re watching a video on a supported media player (mpv, for example). For more details check out &lt;code&gt;man swayidle&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;Miscellaneous bits&lt;/h2&gt;&lt;p&gt;There are a few other cool features I think are worth briefly mentioning:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;bindsym --locked&lt;/code&gt;&lt;/li&gt;&lt;li&gt;swaylock has a config file now&lt;/li&gt;&lt;li&gt;Drag and drop is supported&lt;/li&gt;&lt;li&gt;Rich content (like images) is synced between the Wayland and X11 clipboards&lt;/li&gt;&lt;li&gt;The layout is updated atomically, meaning that you’ll never see an in-progress frame when resizing windows&lt;/li&gt;&lt;li&gt;Primary selection is implemented and synced with X11&lt;/li&gt;&lt;li&gt;Pretty much every long-standing bug has been fixed&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For the full run-down see the &lt;a href=&quot;https://github.com/swaywm/sway/releases/tag/1.0-beta.1&quot; target=&quot;_blank&quot;&gt;release notes&lt;/a&gt;. Give the beta a try, and we’re all looking forward to sway 1.0!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Sway-1.0-highlights/</link>
        
        <pubDate>Sat, 20 Oct 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Sway-1.0-highlights/</guid>
      </item>
    
      <item>
        
        
          <title>Go 1.11 got me to stop ignoring Go</title>
          <description>
            &lt;p&gt;I took a few looks at Go over the years, starting who knows when. My first serious attempt to sit down and learn some damn Go was in 2014, when I set a new personal best at almost 200 lines of code before I got sick of it. I kept returning to Go because I could see how much potential it had, but every time I was turned off for the same reason: &lt;code&gt;GOPATH&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;You see, &lt;code&gt;GOPATH&lt;/code&gt; crossed a line. Go is opinionated, which is fine, but with &lt;code&gt;GOPATH&lt;/code&gt; its opinions extended beyond my Go work and into the rest of my system. As a naive new Go user, I was prepared to accept their opinions on faith - but only within their domain. I already have opinions about how to use my computer. I knew Go was cool, but it could be the second coming of Christ, and so long as it was annoying to use and didn’t integrate with my workflow, I (rightfully) wouldn’t care.&lt;/p&gt;&lt;p&gt;Thankfully Go 1.11 solves this problem, and solves it delightfully well. I can now keep Go’s influence contained to the Go projects I work with, and in that environment I’m much more forgiving of anything it wants to do. And when considered in the vacuum of Go, what it wants to do is really compelling. Go modules are &lt;em&gt;great&lt;/em&gt;, and probably the single best module system I’ve used in any programming language. Go 1.11 took my biggest complaint and turned it into one of my biggest compliments. Now that the One Big Problem is gone, I’ve really started to appreciate Go. Let me tell you about it.&lt;/p&gt;&lt;p&gt;The most important feature of Go is its simplicity. The language is small and it grows a small number of features in each release, which rarely touch the language itself. Some people see this as stagnation, but I see it as stability and I know that very little Go code in the wild, no matter how old, is going to be unidiomatic or fail to compile. Even setting aside stability, the conservative design of the language makes Go code in the wild remarkably consistent. Almost all third-party Go libraries are high quality stuff. Gofmt helps with this as well&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. The limitations of the language and the way the stdlib gently nudges you into good patterns make it easy to write good Go code. Most of the “bad” Go libraries I’ve found are trying to work around Go’s limitations instead of embracing them.&lt;/p&gt;&lt;p&gt;There’s more. The concurrency model is superb. It should come as no surprise that a language built by the alumni of Plan 9 would earn high marks in this regard, and consequently you can scale your Go program up to be as concurrent as you want without even thinking about it. The standard library is also excellent - designed consistently and designed well, and I can count on one hand (or even one finger) the number of stdlib modules I’ve encountered that feel crusty. The type system is great, too. It’s the perfect balance of complexity and simplicity that often effortlessly grants these traits to the abstractions you make with it.&lt;/p&gt;&lt;p&gt;I’m not even slightly bothered by the lack of generics - years as a C programmer taught me not to need them, and I think most of the cases where they’re useful are to serve designs which are too complicated to use anyway. I do have some complaints, though. The concurrency model is great, but a bit too magical and implicit.  Error handling is annoying, especially because finding the origin of the error is unforgivably difficult, but I don’t know how to improve it. The log module leaves a lot to be desired and can’t be changed because of legacy support.  &lt;code&gt;interface{}&lt;/code&gt; is annoying when you have to deal with it, like when dealing with JSON you can’t unmarshall into a struct.&lt;/p&gt;&lt;p&gt;My hope for the future of Go is that it will continue to embrace simplicity in the face of cries for complexity. I consider Go modules a runaway success compared to dep, and I hope to see this story repeated&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; before hastily adding generics, better error handling, etc. Go doesn’t need to compete with anyone like Rust, and trying to will probably ruin what makes Go great. My one request of the Go team: don’t make changes in Go 2.0 which make the APIs of existing libraries unidiomatic.&lt;/p&gt;&lt;p&gt;Though I am growing very fond of it, by no means am I turning into a Go zealot. I still use C, Python, and more all the time and have no intention of stopping. A programming language which tries to fill all niches is a failed programming language. But, to those who were once like me: Go is good now! In fact, it’s great! Try it!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Go-1.11/</link>
        
        <pubDate>Mon, 08 Oct 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Go-1.11/</guid>
      </item>
    
      <item>
        
        
          <title>Don&apos;t sign a CLA</title>
          <description>
            &lt;p&gt;A large minority of open-source projects come with a CLA, or Contributor License Agreement, and require you to sign one before they’ll merge your patch. These agreements typically ask you to go above and beyond the rights you afford the project by contributing under the license the software is distributed with. And you should never sign one.&lt;/p&gt;&lt;p&gt;Free and open source software licenses grant explicit freedoms to three groups: the maintainers, the users, &lt;em&gt;and&lt;/em&gt; the contributors. An important freedom is the freedom to make changes to the software and to distribute these changes to the public. The natural place to do so is by contributing to the upstream project, something a project should be thankful for. A CLA replaces this gratitude with an attempt to weaken these freedoms in a manner which may stand up to the letter of the license, but is far from the spirit.&lt;/p&gt;&lt;p&gt;A CLA is a kick in the groin to a contributor’s good-faith contribution to the project. Many people, myself included, contribute to open source projects under the assumption that my contributions will help serve a project which continues to be open source in perpetuity, and a CLA provides a means for the project maintainers to circumvent that. What the CLA is actually used for is to give the project maintainers the ability to relicense your work under a more restrictive software license, up to and including making it entirely closed source.&lt;/p&gt;&lt;p&gt;We’ve seen this happen before. Consider the Redis Labs debacle, where they adopted the nonfree Anti-Commons Clause&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, and used their CLA to pull along any external contributions for the ride. As thanks for the generous time invested by their community into their software, they yank it out from underneath it and repurpose it to make money with an obscenely nonfree product. Open source is a commitment to your community. Once you make it, you cannot take it back. You don’t get the benefits associated with being an open source project if you have an exit hatch. You may argue that it’s your right to do what you want with your project, but making it open source is &lt;em&gt;explicitly waiving that right&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;So to you, the contributor: if you are contributing to open source and you want it to stay that way, you should not sign a CLA. When you send a patch to a project, you are affording them the same rights they afforded you. The relationship is one of equals. This is a healthy balance. When you sign a CLA, you give them unequal power over you. If you’re scratching an itch and just want to submit the patch in good faith, it’s easy enough to fork the project and put up your changes in a separate place. This is a right afforded to you by every open source license, and it’s easy to do. Anyone who wants to use your work can apply your patches on top of the upstream software. Don’t sign away your rights!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Additional reading: &lt;a href=&quot;https://blog.hansenpartnership.com/gpl-as-the-best-licence-governance-and-philosophy/&quot; target=&quot;_blank&quot;&gt;GPL as the Best Licence – Governance and Philosophy&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Some responses to the discussion around this article:&lt;/p&gt;&lt;p&gt;&lt;em&gt;What about the &lt;a href=&quot;https://www.apache.org/licenses/cla-corporate.txt&quot; target=&quot;_blank&quot;&gt;Apache Foundation CLA&lt;/a&gt;?&lt;/em&gt; This CLA is one of the better ones, because it doesn’t transfer copyright over your work to the Apache Foundation. I have no beef with clauses 1 and 3-8. However, term 2 is too broad and I would not sign this CLA.&lt;/p&gt;&lt;p&gt;&lt;em&gt;What about the Linux kernel &lt;a href=&quot;https://elinux.org/Developer_Certificate_Of_Origin&quot; target=&quot;_blank&quot;&gt;developer certificate of origin&lt;/a&gt;?&lt;/em&gt; I applaud the Linux kernel’s approach here. It covers their bases while still strongly protecting the rights of the patch owner. It’s a short statement with little legalese and little fanfare to agreeing to it (just add “Signed-off By” to your commit message). I approve.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Update April 2021: I wrote a follow-up article about the Developer Certificate of Origin in particular: &lt;a href=&quot;https://drewdevault.com/blog/DCO/&quot;&gt;The Developer Certificate of Origin is a great alternative to a CLA&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Dont-sign-a-CLA/</link>
        
        <pubDate>Fri, 05 Oct 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Dont-sign-a-CLA/</guid>
      </item>
    
      <item>
        
        
          <title>Sway &amp; wlroots at XDC 2018</title>
          <description>
            &lt;p&gt;Just got my first full night of sleep after the return flight from Spain after attending &lt;a href=&quot;https://xdc2018.x.org/&quot; target=&quot;_blank&quot;&gt;XDC 2018&lt;/a&gt;. It was a lot of fun! I attended along with four other major wlroots contributors. Joining me were &lt;a href=&quot;https://github.com/emersion&quot; target=&quot;_blank&quot;&gt;Simon Ser (emersion)&lt;/a&gt; (a volunteer) and &lt;a href=&quot;https://github.com/ascent12&quot; target=&quot;_blank&quot;&gt;Scott Anderson (ascent12)&lt;/a&gt; of &lt;a href=&quot;https://www.collabora.com/&quot; target=&quot;_blank&quot;&gt;Collabora&lt;/a&gt;, who work on both &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt; and &lt;a href=&quot;https://github.com/swaywm/sway&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt;. &lt;a href=&quot;https://github.com/ongy&quot; target=&quot;_blank&quot;&gt;ongy&lt;/a&gt; works on wlroots, &lt;a href=&quot;https://github.com/swaywm/hsroots&quot; target=&quot;_blank&quot;&gt;hsroots&lt;/a&gt;, and &lt;a href=&quot;https://github.com/waymonad/waymonad&quot; target=&quot;_blank&quot;&gt;waymonad&lt;/a&gt;, and joined us on behalf of &lt;a href=&quot;https://www.igel.com/&quot; target=&quot;_blank&quot;&gt;IGEL&lt;/a&gt;. Finally, we were joined by &lt;a href=&quot;https://github.com/agx&quot; target=&quot;_blank&quot;&gt;Guido Günther (agx)&lt;/a&gt; of &lt;a href=&quot;https://puri.sm/&quot; target=&quot;_blank&quot;&gt;Purism&lt;/a&gt;, who works with us on wlroots and on the Librem 5. This was my first time meeting most of them face-to-face!&lt;/p&gt;&lt;p&gt;wlroots was among the highest-level software represented at XDC. Most of the attendees are hacking on the kernel or mesa drivers, and we had a lot to learn from each other. The most directly applicable talk was probably VKMS (virtual kernel mode setting), a work-in-process kernel subsystem which will be useful for testing the complex wlroots DRM code. We had many chances to catch up with the presenters after their talk to learn more and establish a good relationship. We discovered from these chats that some parts of our DRM code was buggy, and have even started onboarding some of them as contributors to sway and wlroots.&lt;/p&gt;&lt;p&gt;We also learned a lot from the other talks, in ways that will pay off over time. One of my favorites was an introduction to the design of Intel GPUs, which went into a great amount of detail into how the machine code for these GPUs worked, why these design decisions make them efficient, and their limitations and inherent challenges. Combined with other talks, we got a lot of insight into the design and function of mesa, graphics drivers, and GPUs. These folks were very available to us for further discussion and clarification after their talks, a recurring theme at XDC and one of the best parts of the conference.&lt;/p&gt;&lt;p&gt;Another recurring theme at XDC was talks about how mesa is tested, with the most in-depth coverage being on Intel’s new CI platform. They provide access to Mesa developers to test their code on &lt;em&gt;every&lt;/em&gt; generation of Intel GPU in the course of about 30 minutes, and brought some concrete data to the table to show that it really works to make their drivers more stable. I took notes that you can expect to turn into builds.sr.ht features! And since these folks were often available for chats afterwards, I think they were taking notes, too.&lt;/p&gt;&lt;p&gt;I also met many of the driver developers from AMD, Intel, and Nvidia; all of whom had interesting insights and were a pleasure to hang out with. In fact, Nvidia’s representatives were the first people I met! On the night of the kick-off party, I led the wlroots clan to the bar for beers and introduced myself to the people who were standing there - who already knew me from my writings critical of Nvidia. Awkward! A productive conversation ensued regardless, where I was sad to conclude that we still aren’t going to see any meaningful involvement in open source from Nvidia. Many of their engineers are open to it, but I think that the engineering culture at Nvidia is unhealthy and that the engineers have very little influence. We made our case and brought up points they weren’t thinking about, and I can only hope they’ll take them home and work on gradually improving the culture.&lt;/p&gt;&lt;p&gt;Unfortunately, Wayland itself was somewhat poorly represented. Daniel Stone (a Wayland &amp; Weston maintainer) was there, and Roman Glig (of KDE), but some KDE folks had to cancel and many people I had hoped to meet were not present. Some of the discussions I wanted to have about protocol standardization and cooperation throughout Wayland didn’t happen. Regardless, the outcome of XDC was very positive - we learned a lot and taught a lot. We found new contributors to our projects, and have been made into new contributors for everyone else’s projects.&lt;/p&gt;&lt;p&gt;Big shoutout to the X Foundation for organizing the event, and to the beautiful city of A Coruña for hosting us, and to University of A Coruña for sharing their university - which consequently led to meeting some students there that used Sway and wanted to contribute! Thanks as well to the generous sponsors, both for sponsoring the event and for sending representatives to give talks and meet the community.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Sway-wlroots-at-XDC-2018/</link>
        
        <pubDate>Sun, 30 Sep 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Sway-wlroots-at-XDC-2018/</guid>
      </item>
    
      <item>
        
        
          <title>Getting started with qemu</title>
          <description>
            &lt;p&gt;I often get asked questions about using my software, particularly sway, on hypervisors like VirtualBox and VMWare, as well as for general advice on which hypervisor to choose. My answer is always the same: qemu. There’s no excuse to use anything other than qemu, in my books. But I can admit that it might be a bit obtuse to understand at first. qemu’s greatest strength is also its greatest weakness: it has so many options that it’s hard to know which ones you need just to get started.&lt;/p&gt;&lt;p&gt;qemu is the swiss army knife of virtualisation, much like ffmpeg is the swiss army knife of multimedia (which comes as no surprise, given that both are written by Fabrice Bellard). I run a dozen permanent VMs with qemu, as well as all of the ephemeral VMs used on &lt;a href=&quot;https://meta.sr.ht&quot; target=&quot;_blank&quot;&gt;builds.sr.ht&lt;/a&gt;. Why is it better than all of the other options? Well, in short: qemu is fast, portable, better supported by guests, and has more features than Hollywood. There’s nothing other hypervisors can do that qemu can’t, and there’s plenty qemu can that they cannot.&lt;/p&gt;&lt;p&gt;Studying the full breadth of qemu’s featureset is something you can do over time. For now, let’s break down a simple Linux guest installation. We’ll start by downloading some install media (how about &lt;a href=&quot;https://alpinelinux.org/&quot; target=&quot;_blank&quot;&gt;Alpine Linux&lt;/a&gt;, I like Alpine Linux) and preparing a virtual hard drive.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;curl -O https://nl.alpinelinux.org/alpine/v3.8/releases/x86_64/alpine-standard-3.8.0-x86_64.iso
qemu-img create -f qcow2 alpine.qcow2 16G
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This makes a 16G virtual hard disk in a file named alpine.qcow2, the qcow2 format being a format which appears to be 16G to the guest (VM), but only actually writes to the host any sectors which were written to by the guest in practice. You can also expose this as a block device on your local system (or a remote system!) with qemu-nbd if you need to. Now let’s boot up a VM using our install media and virtual hard disk:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;qemu-system-x86_64 \
    -enable-kvm \
    -m 2048 \
    -nic user,model=virtio \
    -drive file=alpine.qcow2,media=disk,if=virtio \
    -cdrom alpine-standard-3.8.0-x86_64.iso \
    -sdl
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a lot to take in. Let’s break it down:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;-enable-kvm&lt;/strong&gt;: This enables use of the KVM (kernel virtual machine) subsystem to use hardware accelerated virtualisation on Linux hosts.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;-m 2048&lt;/strong&gt;: This specifies 2048M (2G) of RAM to provide to the guest.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;-nic user,model=virtio&lt;/strong&gt;: Adds a virtual &lt;strong&gt;n&lt;/strong&gt;etwork &lt;strong&gt;i&lt;/strong&gt;nterface &lt;strong&gt;c&lt;/strong&gt;ontroller, using a virtual LAN emulated by qemu. This is the most straightforward way to get internet in a guest, but there are other options (for example, you will probably want to use &lt;code&gt;-nic tap&lt;/code&gt; if you want the guest to do networking directly on the host NIC). &lt;code&gt;model=virtio&lt;/code&gt; specifies a special &lt;code&gt;virtio&lt;/code&gt; NIC model, which is used by the virtio kernel module in the guest to provide faster networking.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;-drive file=alpine.qcow2,media=disk,if=virtio&lt;/strong&gt;: This attaches our virtual disk to the guest. It’ll show up as &lt;code&gt;/dev/vda&lt;/code&gt;. We specify &lt;code&gt;if=virtio&lt;/code&gt; for the same reason we did for &lt;code&gt;-nic&lt;/code&gt;: it’s the fastest interface, but requires special guest support from the Linux virtio kernel module.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;-cdrom alpine-standard-3.8.0-x86_64.iso&lt;/strong&gt; connects a virtual CD drive to the guest and loads our install media into it.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;-sdl&lt;/strong&gt; finally specifies the graphical configuration. We’re using the SDL backend, which is the simplest usable graphical backend. It attaches a display to the guest and shows it in an &lt;a href=&quot;https://www.libsdl.org/&quot; target=&quot;_blank&quot;&gt;SDL&lt;/a&gt; window on the host.&lt;/p&gt;&lt;p&gt;When you run this command, the SDL window will appear and Alpine will boot! You can complete the Alpine installation normally, using &lt;code&gt;setup-alpine&lt;/code&gt; to install it to the attached disk. When you shut down Alpine, run qemu again without &lt;code&gt;-cdrom&lt;/code&gt; to start the VM.&lt;/p&gt;&lt;p&gt;That covers enough to get you off of VirtualBox or whatever other bad hypervisor you’re using. What else is possible with qemu? Here’s a short list of common stuff you can look into:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Running pretty much any guest operating system&lt;/li&gt;&lt;li&gt;Software emulation of non-native architectures like ARM, PPC, RISC-V&lt;/li&gt;&lt;li&gt;Using &lt;code&gt;-spice&lt;/code&gt; instead of &lt;code&gt;-sdl&lt;/code&gt; to enable remote access to the display/keyboard/mouse&lt;/li&gt;&lt;li&gt;Read-only disk images with guest writes stored in RAM (&lt;code&gt;snapshot=on&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;Non-graphical boot with &lt;code&gt;-nographic&lt;/code&gt; and &lt;code&gt;console=ttyS0&lt;/code&gt; configured in your kernel command line&lt;/li&gt;&lt;li&gt;Giving a genuine graphics card to your guest with KVM passthrough for high performance gaming, OpenCL, etc&lt;/li&gt;&lt;li&gt;Using &lt;a href=&quot;https://virt-manager.org/&quot; target=&quot;_blank&quot;&gt;virt-manager&lt;/a&gt; or &lt;a href=&quot;https://help.gnome.org/users/gnome-boxes/stable/&quot; target=&quot;_blank&quot;&gt;Boxes&lt;/a&gt; if you want a GUI to hold your hand&lt;/li&gt;&lt;li&gt;And much more…&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There’s really no excuse to be using any other hypervisor&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. They’re all dogshit compared to qemu.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Getting-started-with-qemu/</link>
        
        <pubDate>Mon, 10 Sep 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Getting-started-with-qemu/</guid>
      </item>
    
      <item>
        
        
          <title>Conservative web development</title>
          <description>
            &lt;p&gt;Today I turned off my ad blocker, enabled JavaScript, opened my network monitor, and clicked the first link on Hacker News - a New York Times article. It started by downloading a megabyte of data as it rendered the page over the course of eight full seconds. The page opens with an advertisement 281 pixels tall, placed before even the title of the article. As I scrolled down, more and more requests were made, downloading a total of 2.8 MB of data with 748 HTTP requests. An article was weaved between a grand total of 1419 vertical pixels of ad space, greater than the vertical resolution of my display. Another 153-pixel ad is shown at the bottom, after the article. Four of the ads were identical.&lt;/p&gt;&lt;p&gt;I was reminded to subscribe three times, for $1/week (after one year this would become $3.75/week). One of these reminders attached itself to the bottom of my screen and followed along as I scrolled. If I scrolled up, it replaced this with a larger banner, which showed me three other articles and an ad. I was asked for my email address once, though I would have had to fill out a captcha to submit it. I took out my phone and repeated the experiment. It took 15 seconds to load, and I estimate the ads took up a vertical space equal to 4 times my phone’s vertical resolution, each ad alone taking up half of my screen.&lt;/p&gt;&lt;p&gt;The text of the article is a total of 9037 bytes, including the title, author, and date. I downloaded the images relevant to the article, including the 1477x1082&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; title image. Before I ran them through an optimizer, they weighed 260 KB; after, 236 KB (using only lossless optimizations). 8% of the total download was dedicated to the content. 5 discrete external companies were informed of my visit to the page and given the opportunity to run artibrary JavaScript on it.&lt;/p&gt;&lt;p&gt;If these are the symptoms, what is the cure? My basic principles are these:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Use no, or very little, JavaScript&lt;/li&gt;&lt;li&gt;Use raster images sparingly, if at all, and optimize them&lt;/li&gt;&lt;li&gt;Provide interactivity with forms and clever CSS&lt;/li&gt;&lt;li&gt;Identify wasted bandwidth and CPU cycles and optimize them&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I’ve been building &lt;a href=&quot;https://meta.sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt; with these principles in mind, and I spent a few hours this optimizing it further. What do the results look like? The heaviest page, &lt;a href=&quot;https://meta.sr.ht&quot; target=&quot;_blank&quot;&gt;the marketing page&lt;/a&gt;, today weighs &lt;strong class=&quot;text-info&quot;&gt;110 KB&lt;/strong&gt; with a cold cache, and &lt;strong
class=&quot;text-danger&quot;&gt;4.6 KB&lt;/strong&gt; warm. &lt;a href=&quot;https://github.com/&quot; target=&quot;_blank&quot;&gt;A similar page&lt;/a&gt; on GitHub.com&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; weighs &lt;strong class=&quot;text-info&quot;&gt;2900 KB&lt;/strong&gt; cold, &lt;strong class=&quot;text-danger&quot;&gt;19.4 KB&lt;/strong&gt; warm. &lt;a href=&quot;https://git.sr.ht/~sircmpwn/linux/tree/master/init/main.c&quot; target=&quot;_blank&quot;&gt;A more typical page&lt;/a&gt; on sr.ht weighs &lt;strong class=&quot;text-info&quot;&gt;56.8 KB&lt;/strong&gt; cold and &lt;strong class=&quot;text-danger&quot;&gt;31.9 KB&lt;/strong&gt; warm, after &lt;strong
class=&quot;text-warning&quot;&gt;2&lt;/strong&gt; HTTP requests; on GitHub &lt;a href=&quot;https://github.com/torvalds/linux/blob/master/init/main.c&quot; target=&quot;_blank&quot;&gt;the same page&lt;/a&gt; is &lt;strong class=&quot;text-info&quot;&gt;781 KB&lt;/strong&gt; cold and &lt;strong class=&quot;text-danger&quot;&gt;57.4 KB&lt;/strong&gt; warm, &lt;strong
class=&quot;text-warning&quot;&gt;118&lt;/strong&gt; requests. This file is 29.1 KB.  The sr.ht overhead is &lt;strong class=&quot;text-info&quot;&gt;27.6 KB&lt;/strong&gt; cold and &lt;strong
class=&quot;text-danger&quot;&gt;2.7 KB&lt;/strong&gt; warm. The GitHub overhead is respectively &lt;strong class=&quot;text-info&quot;&gt;751.9 KB&lt;/strong&gt; and &lt;strong class=&quot;text-danger&quot;&gt;28.2 KB&lt;/strong&gt;. There’s also a 174-pixel-tall ad on GitHub encouraging me to sign up for an account, shown before any of the content.&lt;/p&gt;&lt;p&gt;To be fair, the GitHub page has more features. As far as I can tell, most of these aren’t implemented &lt;em&gt;in&lt;/em&gt; the page, though, and are rather links to other pages. Some of the features &lt;em&gt;in&lt;/em&gt; the page include a dropdown for filtering branches and tags, popups that show detail when you hover over avatars, some basic interactivity in the search, all things that I can’t imagine taking up much space. Does this justify an order of magnitude increase in resource usage?&lt;/p&gt;&lt;p&gt;Honestly, GitHub does a pretty good job overall. Compared to our New York Times example, they’re downright &lt;em&gt;great&lt;/em&gt;. But they could be doing better, and so could we all. You can build beautiful, interactive websites with HTML and CSS alone, supported by a simple backend. Pushing the complexity of rendering your single-page app into the frontend might save you miniscule amounts of server-side performance, but you’d just be offloading the costs onto your visitor’s phone and sucking their battery dry.&lt;/p&gt;&lt;p&gt;There are easy changes you can make. Enable caching on your web server, with a generous expiry. Use a hash of your resources in the URL so that you can bust the cache when you need to. Enable gzip for text resources, and HTTP/2. Run your images through an optimizer, odds are they can be losslessly compressed.  There are harder changes, too. Design your website to be usable without JavaScript, and use small amounts of it to enhance the experience - rather than to &lt;em&gt;be&lt;/em&gt; the experience. Use CSS cleverly to provide interactivity&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. Find ways to offload work to the server where you can&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. Measure your pages to look for places to improve. Challenge yourself to find the simplest way of building the features you want.&lt;/p&gt;&lt;p&gt;And if anyone at Google is reading, you should try recommending these strategies for speeding up pages instead of pushing self-serving faux standards like AMP.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Conservative-web-development/</link>
        
        <pubDate>Tue, 04 Sep 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Conservative-web-development/</guid>
      </item>
    
      <item>
        
        
          <title>How to make a self-hosted video livestream</title>
          <description>
            &lt;p&gt;I have seen some articles in the past which explain how to build the ecosystem &lt;em&gt;around&lt;/em&gt; your video streaming, such as live chat and forums, but which leave the actual video streaming to Twitch.tv. I made a note the last time I saw one of these articles to write one of my own explaining the video bit. As is often the case with video, we’ll be using the excellent &lt;a href=&quot;http://ffmpeg.org/&quot; target=&quot;_blank&quot;&gt;ffmpeg&lt;/a&gt; tool for this. If it’s A/V-related, ffmpeg can probably do it.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: a demonstration video was previously shown here, but as traffic on this article died down I took it offline to reduce unnecessary load.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;ffmpeg has a built-in &lt;a href=&quot;https://dashif.org/&quot; target=&quot;_blank&quot;&gt;DASH&lt;/a&gt; output format, which is the current industry standard for live streaming video to web browsers. It works by splitting the output up into discrete files and using an XML file (an MPD playlist) to tell the player where they are. Few browsers support DASH natively, but &lt;a href=&quot;https://github.com/Dash-Industry-Forum/dash.js/wiki&quot; target=&quot;_blank&quot;&gt;dash.js&lt;/a&gt; can polyfill it by periodically downloading the latest manifest and driving the video element itself.&lt;/p&gt;&lt;p&gt;Getting the source video into ffmpeg is a little bit beyond the scope of this article, but I know some readers won’t be familiar with ffmpeg so I’ll have mercy. Let’s say you want to play some static video files like I’m doing above:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant function&quot;&gt;ffmpeg&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-re&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-stream_loop&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-1&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;my-video.mkv&lt;/span&gt; \
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will tell ffmpeg to read the input (-i) in real time (-re), and loop it indefinitely. If instead you want to, for example, use x11grab instead to capture your screen and pulse to capture desktop audio, try this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;    &lt;span class=&quot;constant function&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;x11grab&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;30&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-video_size&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;1920x1080&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;DISPLAY&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;pulse&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;alsa_input.usb-Blue_Microphones_Yeti_Stereo_Microphone_REV8-00.analog-stereo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This sets the framerate to 30 FPS and the video resolution to 1080p, then reads from the X11 display &lt;code&gt;$DISPLAY&lt;/code&gt; (usually :0). Then we add pulseaudio and use my microphone source name, which I obtained with &lt;code&gt;pactl list sources&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Let’s add some arguments describing the output format. Your typical web browser is a finicky bitch and has some very specific demands from your output format if you want maximum compatability:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;    &lt;span class=&quot;constant function&quot;&gt;-codec:v&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;libx264&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-profile:v&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;baseline&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-level&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;4&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-pix_fmt&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;yuv420p&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-preset&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;veryfast&lt;/span&gt; \
    &lt;span class=&quot;constant&quot;&gt;-codec:a&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;aac&lt;/span&gt; \
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This specifices the libx264 video encoder with the baseline level 4 profile, the most broadly compatible x264 profile, with the yuv420p pixel format, the most broadly compatible pixel format, the veryfast preset to make sure we can encode it in realtime, the aac audio codec. Now that we’ve specified the parameters for the output, let’s configure the output format: DASH.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;	&lt;span class=&quot;constant function&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;dash&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-window_size&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;5&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-remove_at_exit&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;1&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;/tmp/dash/live.mpd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The window_size specifies the maximum number of A/V segments to keep in the manifest at any time, and remove_at_exit will clean up all of the files when ffmpeg exits. The output file is the path to the playlist to write to disk, and the segments will be written next to it. The last step is to serve this with nginx:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;location /dash {
        types {
                application/dash+xml mpd;
                video/mp4 m4v;
                audio/mp4 m4a;
        }
        add_header Access-Control-Allow-Origin *;
        root /tmp;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can now point the &lt;a href=&quot;http://reference.dashif.org/dash.js/nightly/samples/dash-if-reference-player/index.html&quot; target=&quot;_blank&quot;&gt;DASH reference player&lt;/a&gt; at &lt;code&gt;http://your-server.org/dash/live.mpd&lt;/code&gt; and see your video streaming there. Neato! You can add dash.js to your website and you know have a fully self-hosted video live streaming setup ready to rock.&lt;/p&gt;&lt;p&gt;Perhaps the ffmpeg swiss army knife isn’t your cup of tea. If you want to, for example, use &lt;a href=&quot;https://obsproject.com/&quot; target=&quot;_blank&quot;&gt;OBS Studio&lt;/a&gt;, you might want to take a somewhat different approach. The &lt;a href=&quot;https://github.com/arut/nginx-rtmp-module&quot; target=&quot;_blank&quot;&gt;nginx-rtmp-module&lt;/a&gt; provides an RTMP (real-time media protocol) server that integrates with nginx. After adding the DASH output, you’ll end up with something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant function&quot;&gt;rtmp&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;constant function&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;constant function&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;1935&lt;/span&gt;;

        &lt;span class=&quot;constant function&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;live&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;constant function&quot;&gt;dash&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;on&lt;/span&gt;;
            &lt;span class=&quot;constant function&quot;&gt;dash_path&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/tmp/dash&lt;/span&gt;;
            &lt;span class=&quot;constant function&quot;&gt;dash_fragment&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;15s&lt;/span&gt;;
        &lt;span class=&quot;constant function&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;constant function&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can stream to &lt;code&gt;rtmp://your-server.org/live&lt;/code&gt; and your dash segments will show up in &lt;code&gt;/tmp/dash&lt;/code&gt;. There’s no password protection here, so put it in the stream URL (e.g. &lt;code&gt;application R9AyTRfguLK8&lt;/code&gt;) or use an IP whitelist:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;application live {
    allow publish your-ip;
    deny publish all;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you want to get creative with it you can use &lt;a href=&quot;https://github.com/arut/nginx-rtmp-module/wiki/Directives#on_publish&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;on_publish&lt;/code&gt;&lt;/a&gt; to hit an web service with some details and return a non-2xx code to forbid streaming. Have fun!&lt;/p&gt;&lt;p&gt;I learned all of this stuff by making a bot which livestreamed Google hangouts over the LAN to get around the participant limit at work. I’ll do a full writeup about that one later!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Here’s the full script I’m using to generate the live stream on this page:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/tmp/playlist&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/tmp/dash&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;in&lt;/span&gt; /var/www/mirror.sr.ht/hacksway-2018/*
&lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;
	&lt;span class=&quot;constant function&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;string constant&quot;&gt;&amp;quot;file &amp;apos;&lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;apos;&amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /tmp/playlist
&lt;span class=&quot;keyword&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;constant function&quot;&gt;ffmpeg&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-re&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-loglevel&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;error&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-stream_loop&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-1&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;concat&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-safe&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;0&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/tmp/playlist&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-vf&lt;/span&gt; &lt;span class=&quot;string constant&quot;&gt;&amp;quot;drawtext=\
			fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:\
			text=&amp;apos;%{gmtime\:%Y-%m-%d %T} UTC&amp;apos;:\
			fontcolor=white:\
			x=(w-text_w)/2:y=128:\
			box=1:boxcolor=black:\
			fontsize=72,
		drawtext=\
			fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf:\
			text=&amp;apos;REBROADCAST&amp;apos;:\
			fontcolor=white:\
			x=(w-text_w)/2:y=16:\
			box=1:boxcolor=black:\
			fontsize=48&amp;quot;&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-codec:v&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;libx264&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-profile:v&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;baseline&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-pix_fmt&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;yuv420p&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-level&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;4&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-preset&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;veryfast&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-codec:a&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;aac&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;dash&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-window_size&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;5&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;-remove_at_exit&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;1&lt;/span&gt; \
	&lt;span class=&quot;constant&quot;&gt;/tmp/dash/live.mpd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Self-hosted-livestreaming/</link>
        
        <pubDate>Sun, 26 Aug 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Self-hosted-livestreaming/</guid>
      </item>
    
      <item>
        
        
          <title>The Commons Clause will destroy open source</title>
          <description>
            &lt;p&gt;An alarmist title, I know, but it’s true. If the &lt;a href=&quot;https://commonsclause.com/&quot; target=&quot;_blank&quot;&gt;Commons clause&lt;/a&gt; were to be adopted by all open source projects, they would cease to be open source&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, and therefore the Commons clause is trying to destroy open source. When this first appeared I spoke out about it in discussion threads around the net, but didn’t think anyone would take it seriously. Well, yesterday, some parts of Redis &lt;a href=&quot;https://redislabs.com/community/commons-clause/&quot; target=&quot;_blank&quot;&gt;became proprietary software&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The Commons Clause promoted by Kevin Wang presents one of the greatest existential threats to open source I’ve ever seen. It preys on a vulnerability open source maintainers all suffer from, and one I can strongly relate to. It &lt;em&gt;sucks&lt;/em&gt; to not be able to make money from your open source work. It &lt;em&gt;really&lt;/em&gt; sucks when companies are using your work to make money for themselves. If a solution presents itself, it’s tempting to jump at it. But the Commons Clause doesn’t present a solution for supporting open source software. It presents a framework for turning open source software into proprietary software.&lt;/p&gt;&lt;p&gt;What should we do about open source maintainers not getting the funding they need? It’s a very real problem, and one Kevin has &lt;a href=&quot;https://twitter.com/kevinverse/status/1032074268291424257&quot; target=&quot;_blank&quot;&gt;explicitly asked us&lt;/a&gt; to talk about before we criticise his solution to it. I would be happy to share my thoughts. I’ve struggled for many years to find a way to finance myself as the maintainer of many dozens of projects. For a long time it has been a demotivating struggle with no clear solutions, a struggle which at one point probably left me vulnerable to the temptations offered by the Commons Clause. But today, the situation is clearly improving.&lt;/p&gt;&lt;p&gt;Personally, I have a harder go of it because very little of my open source software is appealing to the businesses that have the budget to sponsor them. Instead, I rely on the (much smaller and less stable) recurring donations of my individual users. When I started accepting these, I did not think that it was going to work out. But today, I’m making far more money from these donations than I ever thought possible&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and I see an upwards trend which will eventually lead me to being able to work on open source full time. If I were able to add only a few business-level sponsorships to this equation, I think I would easily have already reached my goals.&lt;/p&gt;&lt;p&gt;There are other options for securing financing for open source, some of which Redis has already been exploring. Selling a hosted and supported version of your service is often a good call. Offering consulting support for your software has also worked for many groups in the past. Some projects succeed with (A)GPL for everyone and BSD for a price. These are all better avenues to explore - making your software proprietary is a tragic alternative that should not be considered.&lt;/p&gt;&lt;p&gt;We need to combine these methods with a greater appreciation for open source in the business community. Businesses need engineers - appeal to your peers so they can appeal to the money on behalf of the projects they depend on. A $250/mo recurring donation to would be a drop in the bucket of most businesses, but a major boon to any open source project, with which the business will almost certainly see tangible value-add as a result. When I get to work today I’m going to identify open source projects we use that accept donations and make the plea&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, and keep making the plea week over week until money is spent. You should, too.&lt;/p&gt;&lt;p&gt;Redis also stands out as a cautionary entry in the history of Contributor License Agreements. Everyone who has contributed to the now-proprietary Redis modules has had their hard work stolen and sold by RedisLabs under a proprietary license. I do not sign CLAs and I think they’re a harmful practice for this very reason. Asking a contributor to sign them is a slap in the face to the good will which led them to make a contribution in the first place. Don’t sign these and don’t ask others to.&lt;/p&gt;&lt;p&gt;I respect antirez&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; very much, but I am sorely disappointed in him. He should have known better and, if you’re reading this, I urge you to roll back your misguided decision. But the Commons Clause is much more deeply disturbing. What Kevin is doing will ruin open source software, maybe for good&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;I really appreciate some of Kevin’s work. &lt;a href=&quot;https://fossa.io/&quot; target=&quot;_blank&quot;&gt;FOSSA&lt;/a&gt; is a really cool tool that can stand to provide some serious value to the open source community. &lt;a href=&quot;https://tldrlegal.com/&quot; target=&quot;_blank&quot;&gt;TL;DR Legal&lt;/a&gt; is a fantastic tool which has already delivered a tremendous amount of value to open source, and I’ve personally referenced it dozens of times. Thank you, honestly, for your work on improving the legal landscape of open source. With Commons Clause, however, Kevin has taken it too far. &lt;a href=&quot;https://www.gnu.org/philosophy/free-sw.en.html&quot; target=&quot;_blank&quot;&gt;The four freedoms&lt;/a&gt; are &lt;em&gt;important&lt;/em&gt;. The only solution is to bury the Commons Clause project. Kill the website and GitHub repository, and we can try to forget this ever happened.&lt;/p&gt;&lt;p&gt;I understand that turning back is going to be hard, which scares me. I know that Kevin has already put a lot of effort into it and convinced himself that it’s the Right Thing To Do. It takes work to write the clause, vet it for legal issues, design a website (a beautiful one, I’ll give you that), and to promote it among your target audience. I know how hard it is to distance yourself from something you’ve staked your personal reputation on. You had only the best intentions&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, Kevin, but please step back from the ego and do the right thing - take this down. You stand to undo all of your hard work for the open source community in one fell swoop with this initiative. I’m begging you, stop while it’s not too late.&lt;/p&gt;&lt;p&gt;Man, two angry articles in a row. I have more technical articles coming up, I promise.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Update 2018-08-23 03:00 UTC:&lt;/strong&gt; Richard Stallman of the Free Software Foundation reached out asking me to clarify the use of “open source” in this article. I have refered to the FSF’s document on essential freedoms as a definition of “open source”. In fact, it is the definition of free software - a distinct concept. The FSF does not advocate for open source software, but particularly for free (or “libre”) software, of which there is some intersection with open source software. For more information on the difference, refer to &lt;a href=&quot;https://www.gnu.org/philosophy/open-source-misses-the-point.html&quot; target=&quot;_blank&quot;&gt;Richard’s article on the subject&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Commons-clause-will-destroy-open-source/</link>
        
        <pubDate>Wed, 22 Aug 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Commons-clause-will-destroy-open-source/</guid>
      </item>
    
      <item>
        
        
          <title>I don&apos;t trust Signal</title>
          <description>
            &lt;p&gt;Occasionally when Signal is in the press and getting a lot of favorable discussion, I feel the need to step into various forums, IRC channels, and so on, and explain why I don’t trust Signal. Let’s do a blog post instead.&lt;/p&gt;&lt;p&gt;Off the bat, let me explain that I expect a tool which claims to be secure to actually be secure. I don’t view “but that makes it harder for the average person” as an acceptable excuse. If Edward Snowden and Bruce Schneier are going to spout the virtues of the app, I expect it to &lt;em&gt;actually&lt;/em&gt; be secure when it matters - when vulnerable people using it to encrypt sensitive communications are targeted by smart and powerful adversaries.&lt;/p&gt;&lt;p&gt;Making promises about security without explaining the tradeoffs you made in order to appeal to the average user is unethical. Tradeoffs are necessary - but self-serving tradeoffs are not, and it’s your responsibility to clearly explain the drawbacks and advantages of the tradeoffs you make. If you make broad and inaccurate statements about your communications product being “secure”, then when the political prisoners who believed you are being tortured and hanged, it’s on you. The stakes are serious. Let me explain why I don’t think Signal takes them seriously.&lt;/p&gt;&lt;h2&gt;Google Play&lt;/h2&gt;&lt;p&gt;Why do I make a big deal out of Google Play and Google Play Services? Well, some people might trust Google, the company. But up against nation states, it’s no contest - Google has ties to the NSA, has been served secret subpoenas, and is literally the world’s largest machine designed for harvesting and analyzing private information about their users. Here’s what Google Play Services &lt;em&gt;actually&lt;/em&gt; is: &lt;strong&gt;a rootkit&lt;/strong&gt;. Google Play Services lets Google do silent background updates on apps on your phone and give them any permission they want. Having Google Play Services on your phone means your phone is not secure.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;For the longest time, Signal wouldn’t work without Google Play Services, but Moxie (the founder of Open Whisper Systems and maintainer of Signal) finally fixed this in 2017. There was also a long time when Signal was only available on the Google Play Store. Today, you can &lt;a href=&quot;https://signal.org/android/apk/&quot; target=&quot;_blank&quot;&gt;download the APK directly from signal.org&lt;/a&gt;, but… well, we’ll get to that in a minute.&lt;/p&gt;&lt;h2&gt;F-Droid&lt;/h2&gt;&lt;p&gt;There’s an alternative to the Play Store for Android. &lt;a href=&quot;https://f-droid.org&quot; target=&quot;_blank&quot;&gt;F-Droid&lt;/a&gt; is an open source app “store” (repository would be a better term here) which only includes open source apps (which Signal thankfully is).  By no means does Signal have to &lt;em&gt;only&lt;/em&gt; be distributed through F-Droid - it’s certainly a compelling alternative. This has been proposed, and Moxie has &lt;a href=&quot;https://github.com/signalapp/Signal-Android/issues/127&quot; target=&quot;_blank&quot;&gt;definitively shut the discussion down&lt;/a&gt;. Admittedly this is from 2013, but his points and the arguments against them haven’t changed. Let me quote some of his positions and my rebuttals:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;No upgrade channel. Timely and automatic updates are perhaps the most effective security feature we could ask for, and not having them would be a real blow for the project.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;F-Droid supports updates. If you’re concerned about moving your updates quickly through the (minimal) bureaucracy of F-Droid, you can always run your own repository. Maybe this is a lot of work?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; I wonder how the workload compares to &lt;a href=&quot;https://signal.org/blog/signal-and-giphy-update/&quot; target=&quot;_blank&quot;&gt;animated gif search&lt;/a&gt;, a very important feature for security concious users. I bet that &lt;a href=&quot;https://signal.org/blog/signal-foundation/&quot; target=&quot;_blank&quot;&gt;50 million dollar donation&lt;/a&gt; could help, given how many people operate F-Droid repositories on a budget of $0.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;No app scanning. The nice thing about market is the server-side APK scanning and signature validation they do. If you start distributing APKs around the internet, it’s a reversion back to the PC security model and all of the malware problems that came with it.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Try searching the Google Play Store for “flashlight” and look at the permissions of the top 5 apps that come up. All of them are harvesting and selling the personal information of their users to advertisers. Is this some kind of joke? F-Droid is a curated repository, like Linux distributions. Google Play is a malware distributor.  Packages on F-Droid are reviewed by a human being and are &lt;a href=&quot;https://f-droid.org/en/docs/Signing_Process/&quot; target=&quot;_blank&quot;&gt;cryptographically signed&lt;/a&gt;. If you run your own F-Droid repo this is even less of a concern.&lt;/p&gt;&lt;p&gt;I’m not going to address all of Moxie’s points here, because there’s a deeper problem to consider. I’ll get into more detail shortly. You can read the 6-year-old threads tearing Moxie’s arguments apart over and over again until GitHub added the feature to lock threads, if you want to see a more in-depth rebuttal.&lt;/p&gt;&lt;h2&gt;The APK direct download&lt;/h2&gt;&lt;p&gt;Last year Moxie added an official APK download to signal.org. He said this was up for “&lt;a href=&quot;https://github.com/signalapp/Signal-Android/issues/127#issuecomment-286223680&quot; target=&quot;_blank&quot;&gt;harm reduction&lt;/a&gt;”, to avoid people using unofficial builds they find around the net. The download page is covered in warnings telling you that it’s for advanced users only, it’s insecure, would you please go to the Google Play store you stupid user. I wonder, has Moxie considered communicating to people the risks of using the Google Play version?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The APK direct download doesn’t even accomplish the stated goal of “harm reduction”. The user has to manually verify the checksum, and figure out how to do it on a phone, no less. A checksum isn’t a signature, by the way - if your government- or workplace- or abusive-spouse-installed certificate authority gets in the way they can replace the APK &lt;em&gt;and&lt;/em&gt; its checksum with whatever they want. The app has to update itself, using a similarly insecure mechanism. F-Droid handles updates and actually signs their packages. This is a no brainer, Moxie, why haven’t you put Signal on F-Droid yet?&lt;/p&gt;&lt;h2&gt;Why is Signal like this?&lt;/h2&gt;&lt;p&gt;So if you don’t like all of this, if you don’t like how Moxie approaches these issues, if you want to use something else, what do you do?&lt;/p&gt;&lt;p&gt;Moxie knows about everything I’ve said in this article. He’s a very smart guy and I am under no illusions that he doesn’t understand everything I’ve put forth. I don’t think that Moxie makes these choices because he thinks they’re the right thing to do. He makes arguments which don’t hold up, derails threads, leans on logical fallacies, and loops back around to long-debunked positions when he runs out of ideas. I think this is deliberate. An open source software team reads this article as a list of things they can improve on and gets started. Moxie reads this and prepares for war. Moxie can’t come out and say it openly, but he’s made the decisions he has made because they serve his own interests.&lt;/p&gt;&lt;p&gt;Lots of organizations which are pretending they don’t make self-serving decisions at their customer’s expense rely on argumentative strategies like Moxie does. If you can put together an argument which on the surface appears reasonable, but requires in-depth discussion to debunk, passerby will be reassured that your position is correct, and that the dissenters are just trolls. They won’t have time to read the lengthy discussion which demonstrates that your conclusions are wrong, especially if you draw the discussion out like Moxie does. It can be hard to distinguish these from genuine positions held by the person you’re talking to, but when it conveniently allows them to make self-serving plays, it’s a big red flag.&lt;/p&gt;&lt;p&gt;This is a strong accusation, I know. The thing which convinced me of its truth is Signal’s centralized design and hostile attitude towards forks. In open source, when a project is making decisions and running things in a way you don’t like, you can always fork the project. This is one of the fundamental rights granted to you by open source. It has a side effect Moxie doesn’t want, however. It reduces his power over the project. Moxie has a clever solution to this: centralized servers and trademarks.&lt;/p&gt;&lt;h2&gt;Trust, federation, and peer-to-peer chat&lt;/h2&gt;&lt;p&gt;Truly secure systems do not require you to trust the service provider. This is the point of end-to-end encryption. But we have to trust that Moxie is running the server software he says he is. We have to trust that he isn’t writing down a list of people we’ve talked to, when, and how often. We have to trust not only that Moxie is trustworthy, but given that Open Whisper Systems is based in San Francisco we have to trust that he hasn’t received a national security letter, too (by the way, Signal doesn’t have a warrant canary). Moxie can &lt;em&gt;tell&lt;/em&gt; us he doesn’t store these things, but he could. &lt;strong&gt;Truly secure systems don’t require trust&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;There are a couple of ways to solve this problem, which can be used in tandem. We can stop Signal from knowing when we’re talking to each other by using peer-to-peer chats. This has some significant drawbacks, namely that both users have to be online at the same time for their messages to be delivered to each other. You can still fall back to peer-to-server-to-peer when one peer is offline, however. But this isn’t the most important of the two solutions.&lt;/p&gt;&lt;p&gt;The most important change is federation. Federated services are like email, in that Alice can send an email from gmail.com to Bob’s yahoo.com address. I should be able to stand up a Signal server, on my own hardware where I am in control of the logs, and communicate freely with other Signal servers, including Open Whisper’s servers. This distributes the security risks across hundreds of operators in many countries with various data extradition laws. This turns what would today be easy for the United States government to break and makes it much, much more difficult. Federation would also open the possibility for bridging the gap with several other open source secure chat platforms to all talk on the same federated network - which would spur competition and be a great move for users of all chat platforms.&lt;/p&gt;&lt;p&gt;Moxie forbids you from distributing branded builds of the Signal app, and if you rebrand he forbids you from using the official Open Whisper servers. Because his servers don’t federate, that means that users of Signal forks &lt;em&gt;cannot talk to Signal users&lt;/em&gt;. This is a truly genius move. No fork of Signal&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; to date has ever gained any traction, and never will, because you can’t talk to any Signal users with them. In fact, there are no third-party applications which can interact with Signal users in any way. Moxie can write as many blog posts which appeal to wispy ideals and “moving ecosystems” as he wants&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, but those are all &lt;em&gt;really&lt;/em&gt; convenient excuses for an argument which allows him to design systems which serve his own interests.&lt;/p&gt;&lt;p&gt;No doubt these are non-trivial problems to solve. But I have &lt;em&gt;personally&lt;/em&gt; been involved in open source projects which have collectively solved similarly difficult problems a thousand times over with a combined budget on the order of tens of thousands of dollars.&lt;/p&gt;&lt;p&gt;What were you going to do with that 50 million dollars again?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Signal/</link>
        
        <pubDate>Wed, 08 Aug 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Signal/</guid>
      </item>
    
      <item>
        
        
          <title>Setting up a local dev mail server</title>
          <description>
            &lt;p&gt;As part of my work on &lt;a href=&quot;https://meta.sr.ht&quot; target=&quot;_blank&quot;&gt;lists.sr.ht&lt;/a&gt;, it was necessary for me to configure a self-contained mail system on localhost that I could test with. I hope that others will go through a similar process in the future when they set up &lt;a href=&quot;https://git.sr.ht/~sircmpwn/lists.sr.ht&quot; target=&quot;_blank&quot;&gt;the code&lt;/a&gt; for hacking on locally or when working on other email related software, so here’s a guide on how you can set it up.&lt;/p&gt;&lt;p&gt;There are lots of things you can set up on a mail server, like virtual mail accounts backed by a relational database, IMAP access, spam filtering, and so on. We’re not going to do any of that in this article - we’re just interested in something we can test our email code with. To start, install your distribution of &lt;code&gt;postfix&lt;/code&gt; and pop open that &lt;code&gt;/etc/postfix/main.cf&lt;/code&gt; file.&lt;/p&gt;&lt;p&gt;Let’s quickly touch on the less interesting config keys to change. If you want the details about how these work, consult the postfix manual.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;myhostname&lt;/em&gt; should be your local hostname&lt;/li&gt;&lt;li&gt;&lt;em&gt;mydomain&lt;/em&gt; should also be your local hostname&lt;/li&gt;&lt;li&gt;&lt;em&gt;mydestination&lt;/em&gt; should be &lt;code&gt;$myhostname, localhost.$mydomain, localhost&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;mynetworks&lt;/em&gt; should be &lt;code&gt;127.0.0.0/8&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;home_mailbox&lt;/em&gt; should be &lt;code&gt;Maildir/&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Also ensure your hostname is set up right in &lt;code&gt;/etc/hosts&lt;/code&gt;, something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;127.0.0.1 homura.localdomain homura
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Okay, those are the easy ones. That just makes it so that your mail server oversees mail delivery for the &lt;code&gt;127.0.0.0/8&lt;/code&gt; network (localhost) and delivers mail to local Unix user mailboxes. It will store incoming email in each user’s home directory at &lt;code&gt;~/Maildir&lt;/code&gt;, and will deliver email to other Unix users. Let’s set up an email client for reading these emails with. Here’s my development &lt;a href=&quot;http://mutt.org&quot; target=&quot;_blank&quot;&gt;mutt&lt;/a&gt; config:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;set edit_headers=yes
set realname=&amp;quot;Drew DeVault&amp;quot;
set from=&amp;quot;sircmpwn@homura&amp;quot;
set editor=vim
set spoolfile=&amp;quot;~/Maildir/&amp;quot;
set folder=&amp;quot;~/Maildir/&amp;quot;
set timeout=5
color index blue default ~P
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Make any necessary edits. If you use mutt to read your normal mail, I suggest also setting up an alias which runs &lt;code&gt;mutt -C path/to/dev/config&lt;/code&gt;. Now, you should be able to send an email to yourself or other Unix accounts with mutt&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Hooray!&lt;/p&gt;&lt;p&gt;To accept email over SMTP, mozy on over to &lt;code&gt;/etc/postfix/master.cf&lt;/code&gt; and uncomment the submission service. You’re looking for something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;127.0.0.1:submission inet n       -       n       -       -       smtpd
#  -o syslog_name=postfix/submission
#  -o smtpd_tls_security_level=encrypt
#  -o smtpd_sasl_auth_enable=yes
#  -o smtpd_tls_auth_only=yes
#  -o smtpd_reject_unlisted_recipient=no
#  -o smtpd_client_restrictions=$mua_client_restrictions
#  -o smtpd_helo_restrictions=$mua_helo_restrictions
#  -o smtpd_sender_restrictions=$mua_sender_restrictions
#  -o smtpd_recipient_restrictions=
#  -o smtpd_relay_restrictions=
#  -o milter_macro_daemon_name=ORIGINATING
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will permit delivery via localhost on the submission port (587) to anyone whose hostname is in &lt;code&gt;$mydestination&lt;/code&gt;. A good old &lt;code&gt;postfix reload&lt;/code&gt; later and you should be able to send yourself an email with SMTP:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ telnet 127.0.0.1 587
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is &amp;apos;^]&amp;apos;.
220 homura ESMTP Postfix
EHLO example.org
250-homura
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 SMTPUTF8
MAIL FROM:&amp;lt;sircmpwn@homura&amp;gt;
250 2.1.0 Ok
RCPT TO:&amp;lt;sircmpwn@homura&amp;gt; 
250 2.1.5 Ok
DATA
354 End data with &amp;lt;CR&amp;gt;&amp;lt;LF&amp;gt;.&amp;lt;CR&amp;gt;&amp;lt;LF&amp;gt;
From: Drew DeVault &amp;lt;sircmpwn@homura&amp;gt;
To: Drew DeVault &amp;lt;sircmpwn@homura&amp;gt;
Subject: Hello world

Hey there 
.
250 2.0.0 Ok: queued as 8267416366B
QUIT
221 2.0.0 Bye
Connection closed by foreign host.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pull up mutt again to read this. Any software which will be sending out mail and speaks SMTP (for example, sr.ht) can be configured now. Last step is to set up LTMP delivery to lists.sr.ht or any other software you want to process incoming emails. I want most mail to deliver normally - I only want LTMP configured for my lists.sr.ht test domain. I’ll set up some transport maps for this purpose. In &lt;code&gt;main.cf&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;local_transport = local:$myhostname
transport_maps = lmdb:/etc/postfix/transport
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then I’ll edit &lt;code&gt;/etc/postfix/transport&lt;/code&gt; and add these lines:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;lists.homura lmtp:unix:/tmp/lists.sr.ht-lmtp.sock
homura local:homura
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will deliver mail normally to &lt;code&gt;$user@homura&lt;/code&gt; (my hostname), but will forward mail sent to &lt;code&gt;$user@lists.homura&lt;/code&gt; to the Unix socket where the &lt;a href=&quot;https://git.sr.ht/~sircmpwn/lists.sr.ht/tree/master/listssrht-lmtp&quot; target=&quot;_blank&quot;&gt;lists.sr.ht LMTP server&lt;/a&gt; lives.&lt;/p&gt;&lt;p&gt;Add the subdomain to &lt;code&gt;/etc/hosts&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;127.0.0.1 lists.homura.localdomain lists.homura
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Run &lt;code&gt;postmap /etc/postfix/transport&lt;/code&gt; and &lt;code&gt;postfix reload&lt;/code&gt; and you’re good to go. If you have the lists.sr.ht daemon working, send some emails to &lt;code&gt;~someone/example-list@lists.$hostname&lt;/code&gt; and you should see them get picked up.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Local-mail-server/</link>
        
        <pubDate>Sun, 05 Aug 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Local-mail-server/</guid>
      </item>
    
      <item>
        
        
          <title>Writing a Wayland compositor with wlroots: shells</title>
          <description>
            &lt;p&gt;I apologise for not writing about wlroots more frequently. I don’t really enjoy working on the McWayface codebase this series of blog posts was originally about, so we’re just going to dismiss that and talk about the various pieces of a Wayland compositor in a more free-form style. I hope you still find it useful!&lt;/p&gt;&lt;p&gt;Today, we’re going to talk about shells. But to make sure we’re on the same page first, a quick refresher on surfaces. A basic primitive of the Wayland protocol is the concept of a “surface”. A surface is a rectangular box of pixels sent from the client to the compositor to display on-screen. A surface can source its pixels from a number of places, including raw pixel data in memory, or opaque handles to GPU resources that can be rendered without copying pixels on the CPU. These surfaces can also evolve over time, using “damage” to indicate which parts have changed to reduce the workload of the compositor when re-rendering them. However, making a surface and filling it with pixels is not enough to get the compositor to show them.&lt;/p&gt;&lt;p&gt;Shells are how surfaces in Wayland are given meaning. Consider that there are several kinds of surfaces you’ll encounter on your desktop. There are application windows, sure, but there are also tooltips, right-click menus and menubars, desktop panels, wallpapers, lock screens, on-screen keyboards, and so on. Each of these has different semantics - your wallpaper cannot be minimized or dragged around and resized, but your application windows can be.  Likewise, your application windows cannot cover the entire screen and soak up all input like your lock screen can. Each of these use cases is fulfilled with a &lt;em&gt;shell&lt;/em&gt;, which generally takes a surface resource, assigns it a role (e.g.  application window), and returns a handle with shell-specific interfaces for manipulating it.&lt;/p&gt;&lt;h2&gt;Shells in wlroots&lt;/h2&gt;&lt;p&gt;I want to first discuss features common to shells as implemented by wlroots. Each shell has a shell-specific interface that sits on top of the surface. Each time a client connects and creates one of these, the shell raises a &lt;code&gt;wl_signal&lt;/code&gt;, &lt;code&gt;events.new_surface&lt;/code&gt;, and passes to it a pointer to a shell-specific structure which encapsulates that shell surface’s state.&lt;/p&gt;&lt;p&gt;Many shells require some configuration between the creation of the shell surface and displaying it on screen. For example, during this period application windows will typically set the window title so that the compositor never has to show an empty title. All Wayland interfaces aim for atomicity, so that all changes are applied in a single fell swoop and we never display an invalid frame. This is why Wayland is known for addressing vsync problems X suffers from, but is pervasive across the ecosystem. Even things like setting the window title are done atomically.&lt;/p&gt;&lt;p&gt;So, once the client is done communiciating the new shell surface’s desired traits to the compositor, it will commit the surface to atomically apply the changes. The first time this happens, the client is ready to be shown, and the shell-specific wlroots shell surface interface will communicate this to you with the surface’s &lt;code&gt;events.map&lt;/code&gt; signal. The reverse is sometimes communicated with &lt;code&gt;events.unmap&lt;/code&gt;, when the shell surface should be hidden.&lt;/p&gt;&lt;h2&gt;xdg-shell&lt;/h2&gt;&lt;p&gt;xdg-shell is currently the only shell whose protocol is considered stable, and it is the shell which describes application windows. You can read the xdg-shell protocol specification (XML) &lt;a href=&quot;https://cgit.freedesktop.org/wayland/wayland-protocols/tree/stable/xdg-shell/xdg-shell.xml&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; (you are strongly encouraged to read through the XML for all protocols mentioned in this article).&lt;/p&gt;&lt;p&gt;The xdg-shell is quite complicated, as it attempts to encapsulate every feature of a typical graphical desktop session in a single protocol. An xdg-shell surface is a &lt;code&gt;wl_surface&lt;/code&gt; wrapped twice - once in a &lt;code&gt;xdg_surface&lt;/code&gt; and then again in a &lt;code&gt;xdg_toplevel&lt;/code&gt; or &lt;code&gt;xdg_popup&lt;/code&gt;, depending on what kind of window it is. The wlroots &lt;code&gt;wlr_xdg_surface&lt;/code&gt; type (the one emitted by &lt;code&gt;xdg_shell.events.new_surface&lt;/code&gt;) contains tagged union of &lt;code&gt;wlr_xdg_toplevel&lt;/code&gt; and &lt;code&gt;wlr_xdg_popup&lt;/code&gt;, selected from the &lt;code&gt;role&lt;/code&gt; field. You can wire up the xdg-shell with &lt;code&gt;wlr_xdg_shell_create&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Most application windows you see are called toplevels. These windows are the root node of a tree of surfaces which may include arbitrarily nested popups, for example, as you navigate through a deep menu. These windows can have titles; parent surfaces; app IDs (e.g. “gnome-calculator”); minimum and maximum sizes; and maximized, minimized, and fullscreen states. They also often&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; draw their own window decorations and drop shadows, and tell the compositor when you click and drag on the titlebar to move or resize the window.  Unfortunately, if the client is not responding or misbehaving, the user cannot use these controls to move, resize, or minimize the window&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;The compositor can tell the window to adopt a specific size, though the client can choose to ignore this. The compositor also lets the client know when it’s “activated”, which is used by GTK+, for example, to start rendering the caret and render a different set of client-side decorations. It can also toggle the fullscreen, minimized, maximized, and other states.&lt;/p&gt;&lt;p&gt;Each of the various state transitions involved are expressed through the &lt;code&gt;wlr_xdg_toplevel.events&lt;/code&gt; signals. The most recent atomically agreed-upon state is stored in &lt;code&gt;wlr_xdg_toplevel.current&lt;/code&gt;. When each of the signals in &lt;code&gt;events&lt;/code&gt; are emitted, the state change will have been applied to &lt;code&gt;client_pending&lt;/code&gt;. However, you must consent to these changes by calling a corresponding function on the xdg_toplevel (e.g. &lt;code&gt;wlr_xdg_toplevel_set_fullscreen&lt;/code&gt;), which will apply the change to &lt;code&gt;server_pending&lt;/code&gt;. You shouldn’t consider these changes atomically set until the &lt;code&gt;wlr_surface.events.commit&lt;/code&gt; signal has been raised. At that point, you can start showing the window in fullscreen or whatever. There’s also some configure/ack-configure stuff going on here which may eventually become relevant to you&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, but wlroots takes care it for the most part.&lt;/p&gt;&lt;p&gt;The popup interface is used to show a “popup” window, which can be used for a variety of purposes. These include context menus (or “right click” menus), tooltips, some confirmation modals&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, etc. The lifecycle of a popup resource is managed similarly to that of a toplevel resource, of course with different states that can be atomically updated. Arguably, the most fundamental of these states is the relative X and Y position of the popup with respect to its parent toplevel surface.&lt;/p&gt;&lt;p&gt;The position of the popup can be influenced by an extraordinarily complicated interface called &lt;code&gt;xdg_positioner&lt;/code&gt;, also provided by xdg-shell. Since these articles focus on the compositor side of things, and they focus on using wlroots, I can thankfully save you from understanding most of the specifics of this interface. The purpose of this interface is to adjust the position and size of &lt;code&gt;xdg_popup&lt;/code&gt; surfaces with respect to the display they live on - for example, to prevent them from being partially off-screen. The rub is that if you’re using wlroots, when the popup is created you can just call &lt;code&gt;wlr_xdg_popup_unconstrain_from_box&lt;/code&gt; to deal with everything, passing it a box which represents the available space surrounding the parent toplevel for the popup to be placed in.&lt;/p&gt;&lt;p&gt;Popups are also able to take “grabs”, which indicate that they should keep focus without respect to any of the other goings-on of the seat. This is used so that you can, for example, use the keyboard to pick items from a context menu. Grabs are automatically handled for you with &lt;code&gt;wlr_seat&lt;/code&gt; for you. If you want to deny or cancel grabs, you can do so through the appropriate &lt;code&gt;wlr_seat&lt;/code&gt; interfaces.&lt;/p&gt;&lt;p&gt;One last note: xdg-shell only recently became stable, so client support for the stable version is hit and miss. The last unstable protocol, xdg-shell v6, is also supported by wlroots. It mostly behaves in the same way. Eventually it will be removed from wlroots.&lt;/p&gt;&lt;h2&gt;layer-shell&lt;/h2&gt;&lt;p&gt;Under the umbrella of wlroots, 8 Wayland compositors have been collaborating on the design of a new shell for desktop shell components. The result is &lt;a href=&quot;https://github.com/swaywm/wlr-protocols/blob/master/unstable/wlr-layer-shell-unstable-v1.xml&quot; target=&quot;_blank&quot;&gt;layer shell (XML)&lt;/a&gt;. The purpose of this shell is to provide an interface for desktop components like panels, lock screens, wallpapers, on-screen keyboards, notifications, and so on, to display on your compositor.&lt;/p&gt;&lt;p&gt;The layer-shell is organized into four discrete layers: background, bottom, top, and overlay, which are rendered in that order. Between bottom and top, application windows are displayed. A wallpaper client might choose to go in the bottom layer, while a notification could show on the top layer, and a panel on the bottom layer.&lt;/p&gt;&lt;p&gt;The compositor’s job is to decide where to place each surface and how large the surface can be. The client can specify either or both of its dimensions (width and height) for the compositor to specify, then provide some hints for the compositor to do so. The client can, for example, choose to be anchored to edges of the screen. A notification might be anchored to &lt;code&gt;TOP | RIGHT&lt;/code&gt;, and a panel might be anchored to &lt;code&gt;LEFT | BOTTOM | RIGHT&lt;/code&gt;. A layer surface anchored to an edge, like our panel, can also request an exclusive zone, which is a number of pixels from the edge that should not be occluded by other layer surfaces or application windows. This is used, for example, when maximizing application windows to prevent them from occluding the panel (or in sway’s case, when arranging tiled windows).&lt;/p&gt;&lt;p&gt;Layer surfaces also have special keyboard input semantics. Some layer surfaces want to receive keyboard input, such as an application launcher overlay. Others might prefer that application windows continue to receive keyboard events, such as a notification. To this end, a layer surface can toggle a boolean indicating its “keyboard interactivity”. For layers beneath application windows, layer surfaces participate in keyboard focus normally, usually meaning they need to be clicked to receive keyboard focus. Above application windows, the top-most layer always has keyboard focus if it requests it.&lt;/p&gt;&lt;p&gt;In wlroots, you can wire up a layer shell to the display with &lt;code&gt;wlr_layer_shell_create&lt;/code&gt;. From there it behaves similarly to xdg-shell with respect to the creation of new surfaces and the handling of atomic state. The main concern of yours is that, when the surface is committed, you need to arrange the surfaces in the affected layer and communicate the final dimensions of the layer surface to the client with &lt;code&gt;wlr_layer_surface_configure&lt;/code&gt;. You can implement the arrangement however you want, but you may find the &lt;a href=&quot;https://github.com/swaywm/sway/blob/master/sway/desktop/layer_shell.c#L18-L215&quot; target=&quot;_blank&quot;&gt;sway implementation&lt;/a&gt; to be a useful reference. Also check out the wlroots &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/master/examples/layer-shell.c&quot; target=&quot;_blank&quot;&gt;example client&lt;/a&gt; to test out your implementation.&lt;/p&gt;&lt;p&gt;Layer surfaces can also have popups, for example when right-clicking on a taskbar. This borrows xdg-shell’s xdg_popup interface, except the parent is set to the layer surface (this is explicitly allowed for through the xdg_popup spec, and you may see future shells doing something similar). Most of your code for xdg_popups can be reused with layer surfaces.&lt;/p&gt;&lt;h2&gt;Xwayland&lt;/h2&gt;&lt;p&gt;Some Wayland developers turn up their nose when I refer to Xwayland as a shell, and perhaps with good reason. However, wlroots treats Xwayland like a shell, so the API remains consistent. For that reason, we’ll treat it as one in this article as well.&lt;/p&gt;&lt;p&gt;We figured that you might be writing a Wayland compositor so that you &lt;em&gt;don’t&lt;/em&gt; have to write an X11 window manager, too. So we wrote one for you, and it’s called &lt;code&gt;wlr_xwayland&lt;/code&gt;. This interface provides an abstraction over Xwayland which makes it behave similarly to our other shells. It still lets you dig your heels into it in any degree so that you can adjust the behavior of your compositor to suit X-specific needs as necessary.&lt;/p&gt;&lt;p&gt;The resulting wlr_xwayland API is similar to the other shells we’ve described. We have a series of events for configuring Xwayland surfaces, a map and unmap event, and we expose a whole bunch of info about Xwayland surfaces so you can make the judgement call about how much or how little to obey their requests (X11 windows make more unreasonable requests than other shells, since X11 was the wild wild west and a lot of clients took advantage of that).&lt;/p&gt;&lt;p&gt;This should be enough to get you started, and if you have questions ask on IRC for the time being. I could go into more detail, but I think Xwayland deserves its own article, and probably not written by me.&lt;/p&gt;&lt;h2&gt;Other shells&lt;/h2&gt;&lt;p&gt;There are three other shells of note. Two are not very interesting:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;wl_shell, the now-deprecated original desktop shell of Wayland&lt;/li&gt;&lt;li&gt;ivi-shell, used for “in-vehicle infotainment” systems running Wayland&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;wlroots supports neither (though I guess we’d accept a patch adding IVI-shell support, maybe if the vehicle industry was open to improving that protocol…), and neither is interesting for desktops, phones, etc. You probably don’t need to worry about them.&lt;/p&gt;&lt;p&gt;The other is the fullscreen-shell, which is used for optimizing the rendering of fullscreen appliations. I don’t know much about how it works, and it’s not supported by wlroots yet; it’s not required of a functional Wayland compositor. Maybe someday!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Wayland-shells/</link>
        
        <pubDate>Sun, 29 Jul 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Wayland-shells/</guid>
      </item>
    
      <item>
        
        
          <title>Git is already federated &amp; decentralized</title>
          <description>
            &lt;p&gt;There have always been murmurs about “replacing GitHub with something decentralized!”, but in the wake of the Microsoft acquisition these murmurs have become conversations. In particular, this blog post is a direct response to forge-net (formerly known as &lt;a href=&quot;https://github.com/git-federation/gitpub&quot; target=&quot;_blank&quot;&gt;GitPub&lt;/a&gt;). They want to federate and decentralize git using ActivityPub, the same technology leveraged by Mastodon and PeerTube. But get this: git is already federated &lt;em&gt;and&lt;/em&gt; decentralized!&lt;/p&gt;&lt;p&gt;I already spoke at length about how a large minority of the git community uses email for collaboration in my &lt;a href=&quot;https://drewdevault.com/2018/07/02/Email-driven-git.html&quot; target=&quot;_blank&quot;&gt;previous article&lt;/a&gt; on the subject. Definitely give it a read if you haven’t already. In this article I want to focus on comparing this model with the possibilities afforded by ActivityPub and provide direction for new forge&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; projects to work towards embracing and improving git’s email-based collaboration tools.&lt;/p&gt;&lt;p&gt;The main issue with using ActivityPub for decentralized git forges boils down to email simply being a better choice. The advantages of email are numerous. It’s already standardized and has countless open source implementations, many in the standard libraries of almost every programming language. It’s decentralized and federated, and it’s &lt;em&gt;already&lt;/em&gt; integrated with git. Has been since day one!  I don’t think that we should replace web forges with our email clients, not at all. Instead, web forges should embrace email to communicate with each other.&lt;/p&gt;&lt;p&gt;Let me give an example of how this could play out. On my platform, &lt;a href=&quot;https://meta.sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt;, users can view their git repositories on the web (duh). One of my goals is to add some UI features here which let them select a range of commits and prepare a patchset for submission via &lt;a href=&quot;https://www.git-scm.com/docs/git-send-email&quot; target=&quot;_blank&quot;&gt;git send-email&lt;/a&gt;. They’ll enter an email address (or addresses) to send the patch(es) to, and we’ll send it along on their behalf.  This email address might be a mailing list on another sr.ht instance in the wild! If so, the email gets recognized as a patch and displayed on the web with a pretty diff and code review tools. Inline comments automatically get formatted as an email response. This shows up in the user’s inbox and sr.ht gets copied on it, showing it on the web again.&lt;/p&gt;&lt;p&gt;I think that workflow looks an awful lot like the workflow forge-net hopes to realize! Here’s where it gets good, though. What if the emails the user puts in are &lt;code&gt;linux-kernel@vger.kernel.org&lt;/code&gt; and a handful of kernel maintainers? Now your git forge can suddenly be used to contribute to the Linux kernel! ActivityPub would build a &lt;em&gt;second&lt;/em&gt;, incompatible federation of projects, while ignoring the already productive federation which powers many of our most important open source projects.&lt;/p&gt;&lt;p&gt;git over email is already supported by a tremendous amount of open source software. There’s tools like &lt;a href=&quot;https://www.gnu.org/software/mailman/&quot; target=&quot;_blank&quot;&gt;mailman&lt;/a&gt; which provide mailing lists and public archives, or &lt;a href=&quot;https://public-inbox.org/&quot; target=&quot;_blank&quot;&gt;public-inbox&lt;/a&gt;, which archives email in git, or &lt;a href=&quot;http://jk.ozlabs.org/projects/patchwork/&quot; target=&quot;_blank&quot;&gt;patchworks&lt;/a&gt; for facilitating code review over email. Some email clients have grown features which make them more suitable for git, such as &lt;a href=&quot;http://mutt.org&quot; target=&quot;_blank&quot;&gt;mutt&lt;/a&gt;. These are the nuts and bolts of hundreds of important projects, including Linux, *BSD, gcc, Clang, postgresql, MariaDb, emacs, vim, ffmpeg, Linux distributions like Debian, Fedora, Arch, Alpine, and countless other projects, including git itself! These projects are incredibly important, foundational projects upon which our open source empire is built, and the tools they use already provide an open, federated protocol for us to talk to.&lt;/p&gt;&lt;p&gt;Not only is email &lt;em&gt;better&lt;/em&gt;, but it’s also &lt;em&gt;easier&lt;/em&gt; to implement. Programming tools for email are very mature. I recently started experimenting with building an ActivityPub service, and it was crazy difficult. I had to write a whole lot of boilerplate and understand new and still-evolving specifications, not to mention setting up a public-facing server with a domain and HTTPs to test federation with other implementations. Email is comparatively easy, it’s built into the standard library. You can shell out to git and feed the patch to the nearest SMTP library in only a handful of lines of code. I bet every single person who reads this article already has an email address, so the setup time approaches zero.&lt;/p&gt;&lt;p&gt;Email also puts the power in the hands of the user right away. On Mastodon there are occasional problems of instance owners tearing down their instance on short notice, taking with them all of their user’s data. If everything is being conducted over email instead, all of the data already lives in the user’s inbox. Freely available tools can take their mail spool and publish a new archive if our services go down. Mail archives can be trivially made redundant across many services. This stuff is seriously resilient to failure. Email was designed when networks were measured in bits per second and often connected through a single unreliable route!&lt;/p&gt;&lt;p&gt;I’m not suggesting that the approach these projects use for collaboration is perfect. I’m suggesting that we should embrace it and solve these problems instead of throwing out the baby with the bathwater. Tools like &lt;code&gt;git send-email&lt;/code&gt; can be confusing at first, which is why we should build tools like web forges that smooth over the process for novices, and write better docs to introduce people to the tools (I recently &lt;a href=&quot;https://man.sr.ht/git.sr.ht/send-email.md&quot; target=&quot;_blank&quot;&gt;wrote a guide&lt;/a&gt; for sr.ht users).&lt;/p&gt;&lt;p&gt;Additionally, many popular email clients have bastardized email to the point where the only way to use git+email for many people starts with abandoning the email client they’re used to using. This can also be solved by having forges send the emails for them, and process the replies. We can also support open source mail clients by building better tools to integrate our emails with them. Setting up the mail servers on the other end can be difficult, too, but we should invest in better mail server software, something which would definitely be valuable even setting aside the matter of project forges.&lt;/p&gt;&lt;p&gt;We need to figure out something for bugs as well, perhaps based on Debian’s work on &lt;a href=&quot;https://www.debian.org/Bugs/&quot; target=&quot;_blank&quot;&gt;Debbugs&lt;/a&gt;. Other areas of development, such as continuous integration, I find are less difficult problems. Many build services already support sending the build results by email, we just need to find a way to get our patches to them (something I’m working on with sr.ht). But we should take these problems one step at a time. Let’s focus on improving the patch workflow git endorses, and as our solutions shake out the best solutions to our other problems will become more and more apparent.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Git-is-already-distributed/</link>
        
        <pubDate>Mon, 23 Jul 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Git-is-already-distributed/</guid>
      </item>
    
      <item>
        
        
          <title>Input handling in wlroots</title>
          <description>
            &lt;p&gt;I’ve said before that wlroots is a “batteries not included” kind of library, and one of the places where that is most apparent is with our approach to input handling. We implemented a very hands-off design for input, in order to support many use-cases: desktop input, phones with and without USB-OTG HIDs plugged in, multiple mice bound to a single cursor, multiple keyboards per seat, simulated input from fake input devices, on-screen keyboards, input which is processed by the compositor but not sent to clients… we support all of these use-cases and even more. However, the drawback of our powerful design is that it’s confusing. Very confusing.&lt;/p&gt;&lt;p&gt;Let’s begin by forgetting about the Wayland part entirely. After all, wlroots is flexible enough that you can use it without writing a Wayland compositor at all! It can be used in a similar fashion to tools like GLFW and SDL, to abstract low-level input (via e.g. libinput) and graphical output (via e.g. DRM). Let’s start here, simply getting input events from wlroots in the first place.&lt;/p&gt;&lt;p&gt;One of the fundamental building blocks of wlroots is the &lt;code&gt;wlr_backend&lt;/code&gt;, which is a resource that abstracts the underlying hardware and exposes a consistent API for outputs and input devices. Outputs have been discussed elsewhere, so let’s focus just on input devices. Each backend provides an event: &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/4984ea49eeaa292d66be9e535d93a4d8185f3e18/include/wlr/backend.h#L17&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_backend.events.new_input&lt;/code&gt;&lt;/a&gt;. The signal is called with a reference to a &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/4984ea49eeaa292d66be9e535d93a4d8185f3e18/include/wlr/types/wlr_input_device.h&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_input_device&lt;/code&gt;&lt;/a&gt; each time a new input device appears on the backend - for example, when you plug a mouse into your computer when using the libinput backend.&lt;/p&gt;&lt;p&gt;The input device can be one of five types, appropriately identified by the &lt;code&gt;type&lt;/code&gt; field. The types are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;WLR_INPUT_DEVICE_KEYBOARD&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;WLR_INPUT_DEVICE_POINTER&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;WLR_INPUT_DEVICE_TOUCH&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;WLR_INPUT_DEVICE_TABLET_TOOL&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;WLR_INPUT_DEVICE_TABLET_PAD&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The type indicates which member of the anonymous union is valid. If &lt;code&gt;wlr_input_device-&amp;gt;type == WLR_INPUT_DEVICE_KEYBOARD&lt;/code&gt;, then &lt;code&gt;wlr_input_device-&amp;gt;keyboard&lt;/code&gt; is a valid pointer to a &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/4984ea49eeaa292d66be9e535d93a4d8185f3e18/include/wlr/types/wlr_keyboard.h&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_keyboard&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Let’s examine the wlr keyboard more closely now. The keyboard struct also provides its own events, like &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;keymap&lt;/code&gt;. If you want to process input from this keyboard, you need to set up an &lt;a href=&quot;https://xkbcommon.org/doc/current/&quot; target=&quot;_blank&quot;&gt;xkbcommon&lt;/a&gt; context for ingesting the raw scancodes emitted by the &lt;code&gt;key&lt;/code&gt; event and converting them to Unicode and keysyms (e.g. “Up”) with an XKB keymap. Most of the wlroots examples &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/4984ea49eeaa292d66be9e535d93a4d8185f3e18/examples/simple.c#L114&quot; target=&quot;_blank&quot;&gt;implement this&lt;/a&gt; if you’re looking for a simple reference.&lt;/p&gt;&lt;p&gt;When these events are sent, we just let you process them as you please. They do not automatically get propagated to any Wayland clients. Communicating these events to the clients is your responsibility, though we provide you tools to help - we’ll get into that shortly. You don’t even have to source the input you give to Wayland clients from a &lt;code&gt;wlr_input_device&lt;/code&gt;, you can just as easily make them up or get them from the network or anywhere else.&lt;/p&gt;&lt;p&gt;Before we get into details on how to send events to clients, let’s examine the other components in your compositor’s input code. First, let’s talk about the cursor.&lt;/p&gt;&lt;p&gt;We provide the &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/4984ea49eeaa292d66be9e535d93a4d8185f3e18/include/wlr/types/wlr_pointer.h&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_pointer&lt;/code&gt;&lt;/a&gt; abstraction for getting events from a “pointer” device, like a mouse. However, because batteries are not included, you will find that we only tell you what the pointer device is doing - we don’t act on it. If you want to, for example, display a cursor image &lt;img
src=&quot;/l.sr.ht/hf39.png&quot; style=&quot;display: inline; margin: 0; padding: 0;&quot; /&gt; on screen which moves around when the mouse does, you need to wire this up yourself. We have tools which can help.&lt;/p&gt;&lt;p&gt;First, let’s talk about getting the cursor image to show. You can source the image from anywhere you want, but you will probably want to leverage &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/master/include/wlr/xcursor.h&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_xcursor&lt;/code&gt;&lt;/a&gt;. This is a small wlroots module (forked from the &lt;code&gt;wayland-cursor&lt;/code&gt; library used by Wayland clients) which can read Xcursor themes, the kind your user will already have installed on their system. Loading up a cursor theme and getting the pixels from it is pretty straightforward. But what should you do with those pixels?&lt;/p&gt;&lt;p&gt;Well, now we have to introduce hardware cursors. Many backends support “hardware” cursors, which is a feature provided by your low-level graphics stack (e.g. GPU drivers) for rendering a cursor on the screen. Hardware cursors are composited by the GPU, which means you can move the cursor around without re-drawing the things underneath it. This is the most energy- and CPU-efficient way of drawing your cursor, and you can do it with &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/4984ea49eeaa292d66be9e535d93a4d8185f3e18/include/wlr/types/wlr_output.h#L179-L190&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_output_cursor_set_image&lt;/code&gt;&lt;/a&gt;, specifying which &lt;code&gt;wlr_output&lt;/code&gt; you want it to appear on and at what coordinates. Not all configurations support hardware cursors, but &lt;code&gt;wlr_output&lt;/code&gt; automatically falls back to software cursors if need be.&lt;/p&gt;&lt;p&gt;Now you have all of the pieces to show a cursor on screen that moves with the mouse. You can store some X and Y coordinates somewhere, grab an image from an Xcursor theme, and throw it at your &lt;code&gt;wlr_output&lt;/code&gt;, then process input events and move it around. Then… you need to consider multiple outputs. And you need to make sure that it can’t be moved outside of an output. And you need to let the user move it around with a drawing tablet or touch screen as well. And… well, it’s about to get complicated. That’s where our next tool comes in!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/swaywm/wlroots/blob/4984ea49eeaa292d66be9e535d93a4d8185f3e18/include/wlr/types/wlr_cursor.h&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_cursor&lt;/code&gt;&lt;/a&gt; is how wlroots saves you from some of this work. It can display a cursor image on-screen, tie it to multiple input devices, constrain it to your outputs and move it across multiple displays. It can also map input from certain devices to certain outputs or regions of the output layout, change the geometry of inputs from a drawing tablet, and more.&lt;/p&gt;&lt;p&gt;To use &lt;code&gt;wlr_cursor&lt;/code&gt;, you should create one (&lt;code&gt;wlr_cursor_create&lt;/code&gt;) and as the backend emits &lt;code&gt;new_input&lt;/code&gt; events, bind them to the cursor with &lt;code&gt;wlr_cursor_attach_input_device&lt;/code&gt;. &lt;code&gt;wlr_cursor&lt;/code&gt; then raises aggregated events from all of its devices, which you can catch and handle accordingly - usually calling a function like &lt;code&gt;wlr_cursor_move&lt;/code&gt; and propagating the event to Wayland clients. You also need to attach a &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/master/include/wlr/types/wlr_output_layout.h&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_output_layout&lt;/code&gt;&lt;/a&gt; to the cursor, so it knows how to constrain the cursor movement and can handle hardware cursors for you.&lt;/p&gt;&lt;p&gt;Aside: the &lt;code&gt;wlr_output_layout&lt;/code&gt; module allows you to configure an arrangement of &lt;code&gt;wlr_output&lt;/code&gt;s in physical space. Its function is fairly straightforward and largely unrelated to our topic - I suggest reading through the header and asking questions if you need help. Once you make one of these and hand it to a &lt;code&gt;wlr_cursor&lt;/code&gt;, you have a cursor on-screen which moves around when you provide input and correctly moves throughout a multi-display setup.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Okay, now that we have all of those pieces in place, we can finally start talking about sending input events to Wayland clients! Before we get into how &lt;em&gt;wlroots&lt;/em&gt; does it, let’s talk about how &lt;em&gt;Wayland&lt;/em&gt; does it in general.&lt;/p&gt;&lt;p&gt;The top-level resource which manages input for a Wayland client is the &lt;code&gt;wl_seat&lt;/code&gt;. One seat, in rough terms, maps to a single set of input devices used by a user (a user who is presumably sitting at a seat in front of their computer). A seat can have up to one keyboard, pointer, touch device, or drawing tablet each. Each of these devices can then &lt;em&gt;enter&lt;/em&gt; or &lt;em&gt;leave&lt;/em&gt; any of the client’s surfaces at the compositor’s orders.&lt;/p&gt;&lt;p&gt;When you bind to a &lt;code&gt;wl_seat&lt;/code&gt;’s &lt;code&gt;wl_keyboard&lt;/code&gt; and &lt;code&gt;wl_keyboard.enter&lt;/code&gt; is raised on a surface, it means your surface has keyboard focus. The compositor will follow-up with (or will have already sent) a &lt;code&gt;wl_keyboard.keymap&lt;/code&gt; signal to let you know the layout of this keyboard (e.g. &lt;code&gt;us-intl&lt;/code&gt;, &lt;code&gt;de&lt;/code&gt;, &lt;code&gt;ru&lt;/code&gt;, etc) in the form of an xkbcommon keymap (the same format we were using with &lt;code&gt;wlr_keyboard&lt;/code&gt; earlier - hint hint). Some number of &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;modifier&lt;/code&gt; events will likely follow as the user taps away.&lt;/p&gt;&lt;p&gt;When you bind to a &lt;code&gt;wl_seat&lt;/code&gt;’s &lt;code&gt;wl_pointer&lt;/code&gt; and &lt;code&gt;wl_pointer.enter&lt;/code&gt; is raised, it means a pointer has moved over one of your surfaces. Note that this can be an entirely separate occasion from receiving keyboard focus. The client is then expected to provide a cursor image to display (at the moment, Wayland &lt;em&gt;requires&lt;/em&gt; client side cursors. They have to do the whole Xcursor dance we did on the wlroots side earlier, too. We have some plans to correct this…). Some number of &lt;code&gt;motion&lt;/code&gt; and &lt;code&gt;button&lt;/code&gt; events will likely follow as the user wiggles their mouse and clicks your windows.&lt;/p&gt;&lt;p&gt;So, how does a wlroots-based compositor facilitate these interactions? With &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/4984ea49eeaa292d66be9e535d93a4d8185f3e18/include/wlr/types/wlr_seat.h&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;wlr_seat&lt;/code&gt;&lt;/a&gt;, our abstraction on top of &lt;code&gt;wl_seat&lt;/code&gt;. This implements the whole &lt;code&gt;wl_seat&lt;/code&gt; state machine, but again leaves it to you to tweak the knobs as you wish. You need to decide how your compositor is going to deal with focus - KDE, Sway, the Librem5 phone UI, an in-vehicle infotainment system; all of these will have a different approach to focus.&lt;/p&gt;&lt;p&gt;wlroots doesn’t render client surfaces for you, and doesn’t know where you put them. Once you figure out where they go, you need to notice when the &lt;code&gt;wlr_cursor&lt;/code&gt; is moved over it and call &lt;code&gt;wlr_seat_pointer_notify_enter&lt;/code&gt; with the pointer’s coordinates relative to the surface it entered, along with any appropriate &lt;code&gt;motion&lt;/code&gt; or &lt;code&gt;button&lt;/code&gt; events through the relevant &lt;code&gt;wlr_seat&lt;/code&gt; functions. The client will also likely send you a cursor image to display - this is done with the &lt;code&gt;wlr_seat.events.request_set_cursor&lt;/code&gt; event.&lt;/p&gt;&lt;p&gt;When you decide that a surface should receive keyboard focus, call &lt;code&gt;wlr_seat_keyboard_notify_enter&lt;/code&gt;. &lt;code&gt;wlr_seat&lt;/code&gt; will automatically handle removing focus from whatever had it last, and will also grab the keymap and send it to the client for you, assuming you configured it with &lt;code&gt;wlr_keyboard_set_keymap&lt;/code&gt;… you did, right? &lt;code&gt;wlr_seat&lt;/code&gt; also semi-transparently deals with grabs, the sort of situation where a client wants to keep keyboard focus for longer than it normally would, to deal with a context menu or something.&lt;/p&gt;&lt;p&gt;Touch events are similar and should be self-explanatory when you read the header. Drawing tablet events are a bit different - they’re not actually specified by the core Wayland protocol. Instead, we rig these up with the &lt;a href=&quot;https://cgit.freedesktop.org/wayland/wayland-protocols/tree/unstable/tablet/tablet-unstable-v2.xml&quot; target=&quot;_blank&quot;&gt;tablet&lt;/a&gt; protocol extension and &lt;a href=&quot;https://github.com/swaywm/wlroots/blob/7f20ab644347b11fd8242beaf7a6fe42c910d014/include/wlr/types/wlr_tablet_v2.h&quot; target=&quot;_blank&quot;&gt;wlr_tablet&lt;/a&gt;. It works in much the same way, but you have to explicitly configure it for a &lt;code&gt;wlr_seat&lt;/code&gt; by calling &lt;code&gt;wlr_tablet_create&lt;/code&gt; yourself.&lt;/p&gt;&lt;p&gt;So, in short, if you wiggle your mouse, here’s what happens:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Before you wiggled your mouse, the &lt;code&gt;libinput&lt;/code&gt; backend noticed it was plugged in and raised a &lt;code&gt;new_input&lt;/code&gt; event.&lt;/li&gt;&lt;li&gt;Your compositor attached the resulting &lt;code&gt;wlr_pointer&lt;/code&gt; to its &lt;code&gt;wlr_cursor&lt;/code&gt;, which it had prepared earlier by looking up an appropriate cursor theme and letting it know about the display layout.&lt;/li&gt;&lt;li&gt;The &lt;code&gt;wlr_pointer&lt;/code&gt; bubbled up a &lt;code&gt;motion&lt;/code&gt; event, which was caught by &lt;code&gt;wlr_cursor&lt;/code&gt; and bubbled up to your compositor.&lt;/li&gt;&lt;li&gt;Your compositor called &lt;code&gt;wlr_cursor_move&lt;/code&gt; to apply the resulting motion, constrained by the output layout, which in turn caused the cursor image on your display to move.&lt;/li&gt;&lt;li&gt;Your compositor then looked around to see if the pointer had moved over any new surfaces. Since wlroots doesn’t handle rendering or know where anything is displayed, this was a rather introspective question.&lt;/li&gt;&lt;li&gt;You &lt;em&gt;did&lt;/em&gt; wiggle it over a new surface, so the compositor called &lt;code&gt;wlr_seat_notify_pointer_enter&lt;/code&gt; after translating the pointer coordinates to surface-local space. It sent a &lt;code&gt;wlr_seat_notify_pointer_motion&lt;/code&gt; for good measure.&lt;/li&gt;&lt;li&gt;The client noticed the pointer entered it and sent back a cursor image to show. The compositor was informed of this via &lt;code&gt;wlr_seat.events.request_set_cursor&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;The compositor handled the client’s cursor image to &lt;code&gt;wlr_cursor&lt;/code&gt;, throwing away all of that hard work loading up a cursor theme just for a client-side cursor to come in and ruin it.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;And there you have it, that’s how input works in wlroots. It’s really fucking complicated, isn’t it? I think this article puts on display both the incredible advantages and serious drawbacks of wlroots. Because you have to plug all of these pieces together yourself, you are afforded an &lt;em&gt;enormous&lt;/em&gt; amount of flexibility. However, you have to do a lot of work and understand a whole lot of different pieces to get there. Libraries like &lt;a href=&quot;https://github.com/Cloudef/wlc&quot; target=&quot;_blank&quot;&gt;wlc&lt;/a&gt; are much easier to use in this respect, but if you want to change even a small detail of this process with wlc you are unable to.&lt;/p&gt;&lt;p&gt;If you have any questions about this article, please reach out to the developers hanging out in &lt;a href=&quot;http://webchat.freenode.net/?channels=sway-devel&amp;uio=d4&quot; target=&quot;_blank&quot;&gt;#sway-devel on irc.freenode.net&lt;/a&gt;. We know this is confusing, and we’re happy to help.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Input-handling-in-wlroots/</link>
        
        <pubDate>Tue, 17 Jul 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Input-handling-in-wlroots/</guid>
      </item>
    
      <item>
        
        
          <title>Simple, correct, fast: in that order</title>
          <description>
            &lt;p&gt;The single most important quality in a piece of software is simplicity. It’s more important than doing the task you set out to achieve. It’s more important than performance. The reason is straightforward: if your solution is not simple, it will not be correct or fast.&lt;/p&gt;&lt;p&gt;Given enough time, you’ll find that all software which solves sufficiently complex problems is going to (1) have bugs and (2) have performance problems. Software with bugs is incorrect. Software with performance problems is not fast. We will face this fact as surely as we will face death and taxes, and we should prepare ourselves accordingly. Let’s consider correctness first.&lt;/p&gt;&lt;p&gt;Complicated software breaks. Simple software is more easily understood and far less prone to breaking: there are less moving pieces, less lines of code to keep in your head, and fewer edge cases. Simple software is more easily tested as well - after all, fewer code paths to run through. Sure, simple software &lt;em&gt;does&lt;/em&gt; break, and when it does the cause and appropriate solution are often apparent.&lt;/p&gt;&lt;p&gt;Now let’s consider performance. You may have some suspicions about your bottlenecks when you set out, and you should consider them in your approach. However, when the performance bill comes due, you’re more likely to have overlooked something than not. The only way to find out for sure what’s slow is to measure. Which is easier to profile: a complicated program, or a simple one? Anyone who’s looked at a big enough flame graph knows exactly what I’m talking about.&lt;/p&gt;&lt;p&gt;Perhaps complicated software once solved a problem. That software needs to be maintained - what is performant and correct today will not be tomorrow. The workload will increase, or the requirements will change. Software is a living thing! When you’re stressed out at 2 AM on Tuesday morning because the server shat itself because your 1,831st new customer pushed the billing system over the edge, do you think you’re well equipped to find the problem in a complex piece of code you last saw a year ago?&lt;/p&gt;&lt;p&gt;When you are faced with these problems, you must seek out the simplest way they can be solved. This may be difficult to do: perhaps the problem is too large, or perhaps you were actually considering the solution before considering the problem. Though difficult it may be, it is your most important job. You need to take problems apart, identify smaller problems within them and ruthlessly remove scope until you find the basic problem you can apply a basic solution to. The complex problem comes later, and it’ll be better served by the composition of simple solutions than with the application of a complex solution.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Simple-correct-fast/</link>
        
        <pubDate>Mon, 09 Jul 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Simple-correct-fast/</guid>
      </item>
    
      <item>
        
        
          <title>The advantages of an email-driven git workflow</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/2.18.0.txt&quot; target=&quot;_blank&quot;&gt;git 2.18.0&lt;/a&gt; has been released, and with it my first contribution to git has shipped! My patch was for a git feature which remains disappointingly obscure: &lt;a href=&quot;https://git-scm.com/docs/git-send-email&quot; target=&quot;_blank&quot;&gt;git send-email&lt;/a&gt;. I want to introduce my readers to this feature and speak to the benefits of using an email-driven git workflow - the workflow git was originally designed for.&lt;/p&gt;&lt;p&gt;Email isn’t as sexy as GitHub (and its imitators), but it has several advantages over the latter. Email is standardized, federated, well-understood, and venerable. A very large body of email-related software exists and is equally reliable and well-understood. You can interact with email using only open source software and customize your workflow at every level of the stack - filtering, organizing, forwarding, replying, and so on; in any manner you choose.&lt;/p&gt;&lt;p&gt;Git has several built-in tools for leveraging email. The first one of note is &lt;a href=&quot;https://git-scm.com/docs/git-format-patch&quot; target=&quot;_blank&quot;&gt;format-patch&lt;/a&gt;. This can take a git commit (or series of commits) and format them as plaintext emails with embedded diffs. Here’s a small example of its output:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;mail&quot;&gt;From 8f5045c871c3060ff5f5f99ce1ada09f4b4cd105 Mon Sep 17 &lt;span class=&quot;markup_environment&quot;&gt;00:00:00 2001&lt;/span&gt;
&lt;span class=&quot;type_builtin&quot;&gt;From: Drew DeVault &lt;/span&gt;&lt;span class=&quot;string type_builtin&quot;&gt;&amp;lt;drew@ddevault.org&amp;gt;&lt;/span&gt;
&lt;span class=&quot;markup_environment&quot;&gt;Date: Wed, 2 May 2018 08:59:27 -0400&lt;/span&gt;
&lt;span class=&quot;tag&quot;&gt;Subject: [PATCH] Silently ignore touch_{motion,up} for unknown ids&lt;/span&gt;

---
 types/wlr_seat.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/types/wlr_seat.c b/types/wlr_seat.c
index f77a492d..975746db 100644
--- a/types/wlr_seat.c
+++ b/types/wlr_seat.c
@@ -1113,7 +1113,6 @@ void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time,
 	struct wlr_seat_touch_grab *grab = seat-&amp;gt;touch_state.grab;
 	struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id);
 	if (!point) {
-		wlr_log(L_ERROR, &amp;quot;got touch up for unknown touch point&amp;quot;);
 		return;
 	}
 
@@ -1128,7 +1127,6 @@ void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time,
 	struct wlr_seat_touch_grab *grab = seat-&amp;gt;touch_state.grab;
 	struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id);
 	if (!point) {
-		wlr_log(L_ERROR, &amp;quot;got touch motion for unknown touch point&amp;quot;);
 		return;
 	}
 
-- 
2.18.0

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;git format-patch is at the bottom of git’s stack of outgoing email features. You can send the emails it generates manually, but usually you’ll use git send-email instead. It logs into the SMTP server of your choice and sends the email for you, after running git format-patch for you and giving you an opportunity to make any edits you like. Given that most popular email clients these days are awful and can’t handle basic tasks like “sending email” properly, I strongly recommend this tool over attempting to send format-patch’s output yourself.&lt;/p&gt;&lt;img style=&quot;max-width: 75%&quot; src=&quot;/l.sr.ht/wmKv.jpg&quot;&gt;

&lt;p style=&quot;text-align: center; max-width: 80%; margin: 1rem auto&quot;&gt;
    &lt;em&gt;
        I put a notch in my keyboard for each person who ignores my advice,
        struggles through sending emails manually, and eventually comes around
        to letting git send-email do it for them.
    &lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;I recommend a few settings to apply to git send-email to make your workflow a bit easier. One is &lt;code&gt;git config --global sendemail.verify off&lt;/code&gt;, which turns off a sometimes-annoying and always-confusing validation step which checks for features only supported by newer SMTP servers - newer, in this case, meaning more recent than November of 1995. I started a thread on the git mailing list this week to discuss changing this option to off by default.&lt;/p&gt;&lt;p&gt;You can also set the default recipient for a given repository by using a local git config: &lt;code&gt;git config sendemail.to admin@example.org&lt;/code&gt;. This lets you skip a step if you send your patches to a consistent destination for that project, like a mailing list. I also recommend &lt;code&gt;git config --global sendemail.annotate yes&lt;/code&gt;, which will always open the emails in your editor to allow you to make changes (you can get this with &lt;code&gt;--annotate&lt;/code&gt; if you don’t want it every time).&lt;/p&gt;&lt;p&gt;The main edit you’ll want to make when annotating is to provide what some call “timely commentary” on your patch. Immediately following the &lt;code&gt;---&lt;/code&gt; after your commit message, you can add a summary of your changes which can be seen by the recipient, but doesn’t appear in the final commit log. This is a useful place to talk about anything useful regarding the testing, review, or integration of your changes. You may also want to edit the &lt;code&gt;[PATCH]&lt;/code&gt; text in the subject line to something like &lt;code&gt;[PATCH v2]&lt;/code&gt; - this can also be done with the &lt;code&gt;-v&lt;/code&gt; flag as well. I also like to add additional To’s, Cc’s, etc at this time.&lt;/p&gt;&lt;p&gt;Git also provides tools for the recipient of your messages. One such tool is &lt;a href=&quot;https://git-scm.com/docs/git-am&quot; target=&quot;_blank&quot;&gt;git am&lt;/a&gt;, which accepts an email prepared with format-patch and integrates it into their repository. Several flags are provided to assist with common integration activities, like signing off on the commit or attempting a 3-way merge. The difficult part can be getting the email to git am in the first place. If you simply use the GMail web UI, this can be difficult. I use &lt;a href=&quot;http://www.mutt.org/&quot; target=&quot;_blank&quot;&gt;mutt&lt;/a&gt;, a TUI email client, to manage incoming patches. This is useful for being able to compose replies with vim rather than fighting some other mail client to write emails the way I want, but more importantly it has the &lt;code&gt;|&lt;/code&gt; key, which prompts you for a command to pipe the email into. Other tools like &lt;a href=&quot;http://www.offlineimap.org/&quot; target=&quot;_blank&quot;&gt;OfflineIMAP&lt;/a&gt; are also useful here.&lt;/p&gt;&lt;p&gt;On the subject of composing replies, reviewing patches is quite easy with the email approach as well. Many bad, yet sadly popular email clients have popularized the idea that the sender’s message is immutable, encouraging you to &lt;a href=&quot;https://en.wikipedia.org/wiki/Posting_style#Top-posting&quot; target=&quot;_blank&quot;&gt;top post&lt;/a&gt; and leave an endlessly growing chain of replies underneath your message. A secret these email clients have kept from you is that you are, in fact, permitted by the mail RFCs to edit the sender’s message as you please when replying - a style called &lt;a href=&quot;https://en.wikipedia.org/wiki/Posting_style#Bottom-posting&quot; target=&quot;_blank&quot;&gt;bottom posting&lt;/a&gt;. I strongly encourage you to get comfortable doing this in general, but it’s essential when reviewing patches received over email.&lt;/p&gt;&lt;p&gt;In this manner, you can dissect the patch and respond to specific parts of it requesting changes or clarifications. It’s just email - you can reply, forward the message, Cc interested parties, start several chains of discussion, and so on. I recently sent the following feedback on a patch I received:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;mail&quot;&gt;&lt;span class=&quot;markup_environment&quot;&gt;Date: Mon, 11 Jun 2018 14:19:22 -0400&lt;/span&gt;
&lt;span class=&quot;type_builtin&quot;&gt;From: Drew DeVault &lt;/span&gt;&lt;span class=&quot;string type_builtin&quot;&gt;&amp;lt;drew@ddevault.org&amp;gt;&lt;/span&gt;
&lt;span class=&quot;type_builtin&quot;&gt;To: Gregory Mullen &lt;/span&gt;&lt;span class=&quot;string type_builtin&quot;&gt;&amp;lt;omitted&amp;gt;&lt;/span&gt;
&lt;span class=&quot;tag&quot;&gt;Subject: Re: [PATCH 2/3 todo] Filter private events from events feed&lt;/span&gt;

On 2018-06-11  9:14 AM, Gregory Mullen wrote:
&amp;gt; diff --git a/todosrht/alembic/versions/cb9732f3364c_clear_defaults_from_tickets_to_support_.py b/todosrht/alembic/versions/cb9732f3364c_clear_defaults_from_tickets_to_support_.py
&amp;gt; -%&amp;lt;-
&amp;gt; +class FlagType(types.TypeDecorator):

I think you can safely import the srht FlagType here without implicating
the entire sr.ht database support code

&amp;gt; diff --git a/todosrht/blueprints/html.py b/todosrht/blueprints/html.py
&amp;gt; -%&amp;lt;-
&amp;gt; +def collect_events(target, count):
&amp;gt; +    events = []
&amp;gt; +    for e in EventNotification.query.filter(EventNotification.user_id == target.id).order_by(EventNotification.created.desc()):

80 cols

I suspect this &amp;apos;collect_events&amp;apos; function can be done entirely in SQL
without having to process permissions in Python and do several SQL
round-trips

&amp;gt;  @html.route(&amp;quot;/~&amp;lt;username&amp;gt;&amp;quot;)
&amp;gt;  def user_GET(username):
&amp;gt; -    print(username)

Whoops! Nice catch.

&amp;gt;      user = User.query.filter(User.username == username.lower()).first()
&amp;gt;      if not user:
&amp;gt;          abort(404)
&amp;gt;      trackers, _ = get_tracker(username, None)
&amp;gt;      # TODO: only show public events (or events the current user can see)

Can remove the comment
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Obviously this isn’t the whole patch we’re seeing - I’ve edited it down to just the parts I want to talk about. I also chose to leave the file names in to aid in navigating my feedback, with casual &lt;code&gt;-%&amp;lt;-&lt;/code&gt; symbols indicating where I had trimmed out parts of the patch. This approach is common and effective.&lt;/p&gt;&lt;p&gt;The main disadvantage of email driven development is that some people are more comfortable working with email in clients which are not well-suited to this kind of work. Popular email clients have caused terrible ideas like HTML email to proliferate, not only enabling spam, privacy leaks, and security vulnerabilities, but also making it more difficult for people to write emails that can be understood by git or tolerated by advanced email users.&lt;/p&gt;&lt;p&gt;I don’t think that the solution to these problems is to leave these powerful tools hanging in the wind and move to less powerful models like GitHub’s pull requests. This is why on my own platform, &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt;, I chose to embrace git’s email-driven approach, and extend it with new tools that make it easier to participate without directly using email. For those like me, I still want the email to be there so you can dig my heels in and do it old-school, but I appreciate that it’s not for everyone.&lt;/p&gt;&lt;p&gt;I started working on the sr.ht mailing list service a couple of weeks ago, which is where these goals will be realized with new email-driven code review tools. My friend &lt;a href=&quot;https://emersion.fr&quot; target=&quot;_blank&quot;&gt;Simon&lt;/a&gt; has been helping out with a Python module named &lt;a href=&quot;https://git.sr.ht/~emersion/python-emailthreads/&quot; target=&quot;_blank&quot;&gt;emailthreads&lt;/a&gt; which can be used to parse email discussions - with a surprising degree of accuracy, considering the flexibility of email. Once I get these tools into a usable state, we’ll likely see sr.ht registrations finally opened to the general public (interested in trying it earlier? &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Email me&lt;/a&gt;). Of course, it’s all &lt;a href=&quot;https://git.sr.ht/~sircmpwn/?search=sr.ht&quot; target=&quot;_blank&quot;&gt;open source&lt;/a&gt;, so you can follow along and try it on your own infrastructure if you like.&lt;/p&gt;&lt;p&gt;Using email for git scales extremely well. The canonical project, of course, is the Linux kernel. A change is made to the Linux kernel an average of 7 times per hour, constantly. It is maintained by dozens of veritable clans of software engineers hacking on dozens of modules, and email allows these changes to efficiently flow code throughout the system. Without email, Linux’s maintenance model would be impossible. It’s worth noting that git was designed for maintaining Linux, of course.&lt;/p&gt;&lt;p&gt;With the right setup, it’s well suited to small projects as well. Sending a patch along for review is a single git command. It lands directly in the maintainer’s inbox and can be integrated with a handful of keystrokes. All of this works without any centralization or proprietary software involved. We should embrace this!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Related articles sent in by readers:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://begriffs.com/posts/2018-06-05-mailing-list-vs-github.html&quot; target=&quot;_blank&quot;&gt;Mailing lists vs Github&lt;/a&gt; by Joe Nelson&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20180522180815/https://dpc.pw/blog/2017/08/youre-using-git-wrong/&quot; target=&quot;_blank&quot;&gt;You’re using git wrong&lt;/a&gt; by Dawid Ciężarkiewicz&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Email-driven-git/</link>
        
        <pubDate>Mon, 02 Jul 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Email-driven-git/</guid>
      </item>
    
      <item>
        
        
          <title>A quick review of my Let&apos;s Encrypt setup</title>
          <description>
            &lt;p&gt;Let’s Encrypt makes TLS much easier for pretty much everyone, but can still be annoying to use. It took me a while to smooth over the cracks in my Let’s Encrypt configuration across my (large) fleet of different TLS-enabled services. I wanted to take a quick moment to share setup with you.&lt;/p&gt;&lt;p&gt;2020-01-02 update: acme-client is unmaintained and caught the BSD disease anyway. I use &lt;a href=&quot;https://github.com/ndilieto/uacme&quot; target=&quot;_blank&quot;&gt;uacme&lt;/a&gt; and my current procedure is documented on my &lt;a href=&quot;https://drewdevault.com/new-server/&quot;&gt;new server checklist&lt;/a&gt;. It might not be exactly applicable to your circumstances, YMMV.&lt;/p&gt;&lt;p&gt;The main components are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kristaps.bsd.lv/acme-client/&quot; target=&quot;_blank&quot;&gt;acme-client&lt;/a&gt;&lt;/li&gt;&lt;li&gt;nginx&lt;/li&gt;&lt;li&gt;cron&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;nginx and cron need no introduction, but acme-client deserves a closer look. The acme client blessed by Let’s Encrypt is &lt;a href=&quot;https://certbot.eff.org/&quot; target=&quot;_blank&quot;&gt;certbot&lt;/a&gt;, but BOY is it complicated. It’s a big ol’ pile of Python and I’ve found it fragile, complicated, and annoying. The goal of maintaining your nginx and apache configs for you is well intentioned but ultimately useless for advanced users. The complexity of certbot is through the roof, and complicated software breaks.&lt;/p&gt;&lt;p&gt;I bounced between alternatives for a while but when I found acme-client, it totally clicked. This one is written in C with minimal dependencies (LibreSSL and libcurl, no brainers IMO). I bring a statically linked acme-client binary with me to new servers and setup time approaches zero as a result.&lt;/p&gt;&lt;p&gt;I use nginx to answer challenges (and for some services, to use the final certificates for HTTPS - did you know you can use Let’s Encrypt for more protocols than just HTTPS?). I quickly &lt;code&gt;mkdir -p /var/www/acme/.well-known/acme-challenge&lt;/code&gt;, make sure nginx can read it, and add the following rules to nginx to handle challenges:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;server {
    listen 80;
    listen [::]:80;
    server_name example.org;

    location ^~ /.well-known/acme-challenge {
        alias /var/www/acme;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If I’m not using the certificates for HTTPS, this is all I need. But assuming I have some kind of website going, the full configuration usually looks more like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;server {
    listen 80;
    listen [::]:80;
    server_name example.org;

    location / {
        return 302 https://$server_name$request_uri;
    }

    location ^~ /.well-known/acme-challenge {
        alias /var/www/acme;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.org;

    ssl_certificate /etc/ssl/acme/$server_name/fullchain.pem;
    ssl_certificate_key /etc/ssl/acme/$server_name/privkey.pem;

    location ^~ /.well-known/acme-challenge {
        alias /var/www/acme;
    }

    # ...application specific rules...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This covers the nginx side of things. To actually do certificate negotiation, I have a simple script I carry around:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant function&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;/var/log/acme &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&amp;gt;&amp;amp;1
&lt;span class=&quot;constant function&quot;&gt;date&lt;/span&gt;

&lt;span class=&quot;function&quot;&gt;acme&lt;/span&gt;() {
    &lt;span class=&quot;property&quot;&gt;site&lt;/span&gt;=&lt;span class=&quot;operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;constant function&quot;&gt;shift&lt;/span&gt;
    &lt;span class=&quot;constant function&quot;&gt;acme-client&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-vNn&lt;/span&gt; \
        &lt;span class=&quot;constant&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/etc/ssl/acme/&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;/&lt;/span&gt; \
        &lt;span class=&quot;constant&quot;&gt;-k&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/etc/ssl/acme/&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;/privkey.pem&lt;/span&gt; \
        &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;site&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;*&lt;/span&gt;
}

&lt;span class=&quot;constant function&quot;&gt;acme&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;example.org&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;subd1.example.org&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;subd2.example.org&lt;/span&gt;

&lt;span class=&quot;constant function&quot;&gt;nginx&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first two lines set up a log file in &lt;code&gt;/var/log/acme&lt;/code&gt; I can use to debug any issues that arise. Then I have a little helper function that wires up acme-client the way I like it, and I can call it for each domain I need certs for on this server. The last line changes if I’m doing something other than HTTPS with the certs (for example, &lt;code&gt;postfix reload&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;One gotcha is that acme-client will bail out if the directories don’t exist when you run it, so a quick &lt;code&gt;mkdir -p /etc/ssl/acme/example.org&lt;/code&gt; when adding new sites is necessary&lt;/p&gt;&lt;p&gt;The final step is a simple cron entry that runs the script daily:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;0 0 * * * /usr/local/bin/acme-update-certs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It’s that easy. It took me a while to get a Let’s Encrypt setup that was simple and satisfactory, but I believe I’ve settled on this one. I hope you find it useful!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/My-lets-encrypt-setup/</link>
        
        <pubDate>Wed, 27 Jun 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/My-lets-encrypt-setup/</guid>
      </item>
    
      <item>
        
        
          <title>Should you move from GitHub to sr.ht</title>
          <description>
            &lt;p&gt;I’m not terribly concerned about Microsoft’s acquisition of GitHub, but I don’t fault those who are worried. I’ve been working on my alternative platform, &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt;, for quite a while. I’m not about to leave GitHub because of Microsoft alone. I do have some political disagreements with GitHub and Microsoft, but those are also not the main reason that I’m building sr.ht. I simply think I can do it better. If my approach aligns with your needs, then sr.ht may be the platform for you.&lt;/p&gt;&lt;p&gt;There are several GitHub alternatives, but for the most part they’re basically GitHub rip-offs. Unlike GitLab, Gogs/Gitea, BitBucket; I don’t see the GitHub UX as the pinnacle of project hosting - there are many design choices (notably pull requests) which I think have lots of room for improvement. sr.ht instead embraces git more closely, for example building &lt;em&gt;on top&lt;/em&gt; of email rather than &lt;em&gt;instead of&lt;/em&gt; email.&lt;/p&gt;&lt;p&gt;GitHub optimizes for the end-user and the drive-by contributor. sr.ht optimizes for the maintainers and core contributors instead. We have patch queues and ticket queues which you can set up automated filters in or manually curate, and are reusable for projects on external platforms. You have tools which allow you to customize the views you see separately from the views visitors see, like bugzilla-style custom ticket searches. Our CI service gives you KVM virtualization and knobs you can tweak to run sophisticated automation for your project. Finally, all of it is &lt;a href=&quot;https://git.sr.ht/~sircmpwn/?search=sr.ht&quot; target=&quot;_blank&quot;&gt;open source&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The business model is also something I think I can do better. GitHub and GitLab are both VC-funded and trapped into appeasing their shareholders (or now, in GitHub’s case, the needs of Microsoft as a whole). I think this leads to incentives which don’t align with the users, as it’s often more important to support the bottom line than to build what the users want or need. Rather than trying to raise as much money as possible, the sr.ht aims to be more a grassroots platform. I’m still working on the money details, but each user will be expected to pay a subscription fee and growth will be artificially slowed if necessary to make sure the infrastructure can keep up. In my opinion, venture capital does not lead to healthy businesses or a healthy economy on the whole, and I think the users suffer for it. My approach is different.&lt;/p&gt;&lt;p&gt;As for my own projects and the plan for moving them, I don’t intend to move anything until it won’t be disruptive to the project. I’ve been collecting feedback from co-maintainers and core contributors to each of the projects I expect to move and using this feedback to drive sr.ht priorities. They will eventually move, but only when it’s ready.&lt;/p&gt;&lt;p&gt;I intend to open sr.ht to the public soon, once I have a billing system in place and break ground on mailing lists (among some smaller improvements). If anyone is interested in checking it out prior to the public release, shoot me an email at &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;drew@ddevault.org&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Should-you-move-to-sr.ht/</link>
        
        <pubDate>Tue, 05 Jun 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Should-you-move-to-sr.ht/</guid>
      </item>
    
      <item>
        
        
          <title>How I maintain FOSS projects</title>
          <description>
            &lt;p&gt;Today’s is another blog post which has been on my to-write list for a while. I have hesitated a bit to write about this, because I’m certain that my approach isn’t perfect. I think it’s pretty good, though, and people who work with me in FOSS agreed after a quick survey. So! Let’s at least put it out there and discuss it.&lt;/p&gt;&lt;p&gt;There are a few central principles I use to guide my maintainership work:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Everyone is a volunteer and should be treated as such.&lt;/li&gt;&lt;li&gt;One patch is worth a thousand bug reports.&lt;/li&gt;&lt;li&gt;Empower people to do what they enjoy and are good at.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The first point is very important. My open source projects are not the work of a profitable organization which publishes open source software as a means of giving back. Each of these projects is built and maintained entirely by volunteers. Acknowledging this is important for keeping people interested in working on the project - you can never expect someone to volunteer for work they aren’t enjoying&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. I am always grateful for any level of involvement a person wants to have in the project.&lt;/p&gt;&lt;p&gt;Because everyone is a volunteer, I encourage people to work on their own agendas, on their own schedule and at their own pace. None of our projects are in a hurry, so if someone is starting to get burnt out, they should have no reservations about taking a break for as long as they wish. I’d rather have something done slowly, correctly, and by a contributor who is enjoying their work than quickly and by a contributor who is burnt out and stressed. No one should ever be stressed out because of their involvement in the project. Some of it is unavoidable - especially where politics is involved - but I don’t hold grudges against anyone who steps away and I try to shoulder the brunt of the bullshit myself.&lt;/p&gt;&lt;p&gt;The second principle is closely related to the first. If a bug does not affect someone who works on the project and the problem doesn’t interest anyone who works on the project, it’s probably not going to get fixed. I would much rather help someone familiarize themselves with the codebase and tooling necessary for them to solve their own problems and send a patch, even if it takes ten times longer than fixing the bug myself. I have never found a user who, even if they aren’t comfortable with programming or the specific technologies in use, has been unable to solve a problem which they were willing to invest time into and ask questions about.&lt;/p&gt;&lt;p&gt;This principle often leads to conflict with users whose bugs don’t get fixed, but I stick to it. I would rather lose every user who is unwilling to attempt a patch than invest the resources of my contributors into work they’re uninterested in. In the long term, the health of the project is far better if I always have developers engaged in and enjoying their work on it than if I lose users who are upset by my approach.&lt;/p&gt;&lt;p&gt;These first two principles don’t affect my day-to-day open source work so much as they set the tone for it. The third principle, however, constitutes most of my job as a maintainer, and it’s with it that I add the most value. My main role is to empower people who contribute to do work they enjoy, which benefits the project, and which keeps them interested in coming back to do more.&lt;/p&gt;&lt;p&gt;Finding things people enjoy working on is the main task in this role. Once people have made a few contributions, I can get an idea of how they like to work and what they’re good at, and help them find things to do which play to their strengths. Supporting a contributors potential is important as well, and if someone expresses interest in certain kinds of work or I think they show promise in an area, it’s my responsibility to help them find work to nurture these skills and connect them with good mentors to help.&lt;/p&gt;&lt;p&gt;This starts to play in another major responsibility I have as a maintainer, which is facilitating effective communication throughout the project. As people grow in the project they generally become effective at managing communication themselves, but new contributors appear all the time. A major responsibility as a maintainer is connecting new contributors to domain experts in a problem, or to users who can reproduce problems or are willing to test their patches.&lt;/p&gt;&lt;p&gt;I’m also responsible for keeping up with each contributor’s growth in the project. For those who are good at and enjoy having responsibility in the project, I try to help them find it. As contributors gain a better understanding of the code, they’re trusted to handle large features with less handholding and perform more complex work&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Often contributors are given opportunities to become better code reviewers, and usually get merge rights once they’re good at it. Things like commit access are a never a function of rank or status, but of enabling people to do the things that they’re good at.&lt;/p&gt;&lt;p&gt;It’s also useful to remember that your projects are not the only game in town. I frequently encourage people who contribute to contribute to other projects as well, and I personally try to find ways to contribute back to their own projects (though not as much as I’d often like to). I offer support as a sysadmin to many projects started by contributors to my projects and I send patches whenever I can. This pays directly back to the project in the form of contributors with deeper and more diverse experience. It’s also fun to take a break from working on the same stuff all the time!&lt;/p&gt;&lt;p&gt;There’s also some work that someone’s just gotta do, and that someone is usually me. I have to be a sysadmin for the websites, build infrastructure, and so on. If there are finances, I have to manage them. I provide some kind of vision for the project and decide what work is in scope. There’s also some boring stuff like preparing changelogs and release notes and shipping new versions, or liaising with distros on packages. I also end up being responsible for any marketing.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Getting and supporting contributors is the single most important thing you can do for your project as a maintainer. I often get asked how I’m as productive as I seem to be. While I can’t deny that I can write a lot of code, it’s peanuts compared to the impact made by other contributors. I get a lot of credit for sway, but in reality I’ve only written 1-3 sway commits per week in the past few months. For this reason, the best approach focuses on the contributors, to whom I owe a great debt of gratitude.&lt;/p&gt;&lt;p&gt;I’m still learning, too! I speak to contributors about my approach from time to time and ask for feedback, and I definitely make mistakes. I hope that I’ll receive more feedback soon after some of them read this blog post, too. My approach will continue to grow over time (hopefully for the better) and I hope our work will enjoy success as a result.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-I-maintain-FOSS-projects/</link>
        
        <pubDate>Fri, 01 Jun 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-I-maintain-FOSS-projects/</guid>
      </item>
    
      <item>
        
        
          <title>Embedding files in C programs with koio</title>
          <description>
            &lt;p&gt;Quick blog post today to introduce a new tool I wrote: &lt;a href=&quot;https://git.sr.ht/~sircmpwn/koio&quot; target=&quot;_blank&quot;&gt;koio&lt;/a&gt;. This is a small tool which takes a list of files and embeds them in a C file. A library provides an fopen shim which checks the list of embedded files before resorting to the real filesystem.&lt;/p&gt;&lt;p&gt;I made this tool for &lt;a href=&quot;https://github.com/SirCmpwn/chopsui&quot; target=&quot;_blank&quot;&gt;chopsui&lt;/a&gt;, where I eventually want to be able to bundle up sui markup, stylesheets, images, and so on in a statically linked chopsui program. Many projects have small tools which serve a similar purpose, but it was simple enough and useful enough that I chose to make something generic so it could be used on several projects.&lt;/p&gt;&lt;p&gt;The usage is pretty simple. I can embed &lt;code&gt;ko_fopen.c&lt;/code&gt; in a C file with this command:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;constant operator function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant function&quot;&gt; &lt;/span&gt;&lt;span class=&quot;constant property function&quot;&gt;koio&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;bundle.c&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ko_fopen.c://ko_fopen.c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can compile and link with &lt;code&gt;bundle.c&lt;/code&gt; and do something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;koio.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;koio_load_assets&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;koio_unload_assets&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;main&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;argc&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;argv&lt;/span&gt;) {
    &lt;span class=&quot;constant variable function&quot;&gt;koio_load_assets&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;ko_fopen&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;//ko_fopen.c&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;r&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; ((&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;fgetc&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;)) &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;EOF&lt;/span&gt;) {
        &lt;span class=&quot;constant variable function&quot;&gt;putchar&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
    &lt;span class=&quot;constant variable function&quot;&gt;fclose&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable function&quot;&gt;koio_unload_assets&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The generated &lt;code&gt;bundle.c&lt;/code&gt; looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;koio.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; {
	&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
} &lt;span class=&quot;constant variable&quot;&gt;files&lt;/span&gt;[] &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; {
	{
		&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;//ko_fopen.c&amp;quot;&lt;/span&gt;,
		&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;408&lt;/span&gt;,
		&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;quot;#define _POSIX_C_SOURCE 200809L\n#include &amp;lt;errno.h&amp;gt;\n#include &amp;lt;stdlib.h&amp;gt;\n#inc&amp;quot;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;quot;lude &amp;lt;stdio.h&amp;gt;\n#include \&amp;quot;koio_private.h\&amp;quot;\n\nFILE *ko_fopen(const char *path&amp;quot;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;quot;, const char *mode) {\n\tstruct file_entry *entry = hashtable_get(&amp;amp;koio_vfs, p&amp;quot;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;quot;ath);\n\tif (entry) {\n\t\tif (mode[0] != &amp;apos;r&amp;apos; || mode[1] != &amp;apos;\\0&amp;apos;) {\n\t\t\ter&amp;quot;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;quot;rno = ENOTSUP;\n\t\t\treturn NULL;\n\t\t}\n\t\treturn fmemopen(entry-&amp;gt;data, en&amp;quot;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;quot;try-&amp;gt;len, \&amp;quot;r\&amp;quot;);\n\t}\n\treturn fopen(path, mode);\n}\n&amp;quot;&lt;/span&gt;,
	},
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;koio_load_assets&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;) {
	&lt;span class=&quot;constant variable function&quot;&gt;ko_add_file&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;files&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;path&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;files&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;data&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;files&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;len&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;koio_unload_assets&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;) {
	&lt;span class=&quot;constant variable function&quot;&gt;ko_del_file&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;files&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;path&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A very simple tool, but one that I hope people will find useful. It’s very lightweight:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;312 lines of C&lt;/li&gt;&lt;li&gt;/bin/koio is ~40 KiB statically linked to musl&lt;/li&gt;&lt;li&gt;libkoio.a is ~18 KiB&lt;/li&gt;&lt;li&gt;Only mandatory dependencies are POSIX 2008 and a C99 compiler&lt;/li&gt;&lt;li&gt;Only optional dependency is &lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc&quot; target=&quot;_blank&quot;&gt;scdoc&lt;/a&gt; for the manual, which is similarly lightweight&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Enjoy!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Embedding-files-in-C/</link>
        
        <pubDate>Tue, 29 May 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Embedding-files-in-C/</guid>
      </item>
    
      <item>
        
        
          <title>Why did we replace wlc?</title>
          <description>
            &lt;p&gt;For a little over a year, I’ve been working with a bunch of talented C developers to build a replacement for the &lt;a href=&quot;https://github.com/Cloudef/wlc&quot; target=&quot;_blank&quot;&gt;wlc&lt;/a&gt; library. The result is &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;, and we’re still working on completing it and updating our software to use it. The &lt;a href=&quot;https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/&quot; target=&quot;_blank&quot;&gt;conventional wisdom&lt;/a&gt; suggests that rewriting your code from scratch is almost never the right idea. So why did we do it, and how is it working out? I have spoken a little about this in the past, but we’ll answer this question in detail today.&lt;/p&gt;&lt;p&gt;Sway will have been around for 3 years as of this August. When I started the project, I wanted to skip some of the hard parts and get directly to implementing i3 features. To this end, I was browsing around for libraries which provided some of the low-level plumbing for me - stuff like DRM (Display Resource Management) and KMS (Kernel Mode Setting), EGL and GLES wires, libinput support, and so on. I was more interested in whatever tool could get me up to speed and writing sway-specific code quickly. My options at this point came down to wlc and &lt;a href=&quot;https://github.com/michaelforney/swc&quot; target=&quot;_blank&quot;&gt;swc&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;swc’s design is a little bit better in retrospect, but I ended up choosing wlc for the simple reason that it had an X11 backend I could use for easier debugging. If I had used swc, I would have been forced to work without a display server and test everything under the DRM backend - which would have been pretty annoying. So I chose wlc and go to work.&lt;/p&gt;&lt;p&gt;Designwise, wlc is basically a Wayland compositor with a plugin API, except you get to write &lt;code&gt;main&lt;/code&gt; yourself and the plugin API communicates entirely in-process. wlc has its own renderer (which you cannot control) and its own desktop with its own view abstraction (which you cannot control). You have some events that it bubbles up for you and you can make some choices like where to arrange windows.  However, if you just wire up some basics and run &lt;code&gt;wlc_init&lt;/code&gt;, wlc will do all of the rest of the work and immediately start accepting clients, rendering windows, and dispatching input.&lt;/p&gt;&lt;p&gt;Over time we were able to make some small improvements to wlc, but sway 0.x still works with these basic principles today. Though this worked well at first, over time more and more of sway’s bugs and limitations were reflections of problems with wlc. A lengthy discussion on IRC and &lt;a href=&quot;https://github.com/swaywm/sway/issues/1076&quot; target=&quot;_blank&quot;&gt;on GitHub&lt;/a&gt; ensued and we debated for several weeks on how we should proceed. I was originally planning on building a new compositor entirely in-house (similar to GNOME’s mutter and KDE’s kwin), and I wanted to abstract the i3-specific functionality of sway into some kind of plugin. Then, more “frontends” could be written on top of sway to add functionality like AwesomeWM, bspwm, Xmonad, etc.&lt;/p&gt;&lt;p&gt;After some discussion among the sway team and with other Wayland compositor projects &lt;a href=&quot;https://github.com/way-cooler/way-cooler/issues/248&quot; target=&quot;_blank&quot;&gt;facing similar problems&lt;/a&gt; with wlc, I decided that we would start developing a standalone library to replace wlc instead, and with it allow a more diverse Wayland ecosystem to flourish. Contrary to wlc’s design - a Wayland compositor with some knobs - wlroots is a set of modular tools with which you build the Wayland compositor yourself. This design allows it to be suited to a huge variety of projects, and as a result it’s now being used for many different Wayland compositors, each with their own needs and their own approach to leveraging wlroots.&lt;/p&gt;&lt;p&gt;When we started working on this, I wasn’t sure if it was going to be successful. Work began slowly and I knew we had a monumental task ahead of us. We spent a lot of time and a few large refactorings getting a feel for how we wanted the library to take shape. Different parts matured at different paces, sometimes with changes in one area causing us to rethink design decisions that affected the whole project. Eventually, we fell into our stride and found an approach that we’re very happy with today.&lt;/p&gt;&lt;p&gt;I think that the main difference with the approach that wlroots takes comes from experience. Each of the people working on sway, wlc, way cooler, and so on were writing Wayland compositors for the first time. I’d say the problems that arose as a result can also be seen throughout other projects, including Weston, KWin, and so on. The problem is that when we all set out, we didn’t fully understand the opportunities afforded by Wayland’s design, nor did we see how best to approach tying together the rather complicated Linux desktop stack into a cohesive project.&lt;/p&gt;&lt;p&gt;We could have continued to maintain wlc, fixed bugs, refactored parts of it, and maybe eventually arrived at a place where sway more or less worked. But we’d simply be carrying on the X11 tradition we’ve been trying to escape this whole time. wlc was a kludge and replacing it was well worth the effort - it simply could not have scaled to the places where wlroots is going. Today, wlroots is the driving force behind 6 Wayland compositors and is targeting desktops, tablets, and phones. Novel features never seen on any desktop - even beyond Linux - are possible with this work. Now we can think about not only replacing X11, but innovating in ways it never could have.&lt;/p&gt;&lt;p&gt;Our new approach is the way that Wayland compositors should be made. wlroots is the realization of Wayland’s potential. I am hopeful that our design decisions will have a lasting positive impact on the Wayland ecosystem.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Why-rewrite-wlc/</link>
        
        <pubDate>Sun, 27 May 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Why-rewrite-wlc/</guid>
      </item>
    
      <item>
        
        
          <title>Introducing scdoc, a man page generator</title>
          <description>
            &lt;p&gt;A man page generator is one of those tools that I’ve said I would write for a long time, being displeased with most of the other options. For a while I used asciidoc, but was never fond of it. There are a few things I want to see in a man page generator:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;A syntax which is easy to read and write&lt;/li&gt;&lt;li&gt;Small and with minimal dependencies&lt;/li&gt;&lt;li&gt;Designed with man pages as a first-class target&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;All of the existing tools failed some of these criteria. &lt;a href=&quot;http://asciidoc.org/&quot; target=&quot;_blank&quot;&gt;asciidoc&lt;/a&gt; hits #1, but fails #2 and #3 by being written in XSLT+Python and targetting man pages as a second-class citizen. &lt;a href=&quot;http://mandoc.bsd.lv/&quot; target=&quot;_blank&quot;&gt;mdocml&lt;/a&gt; fails #1 (it’s not much better than writing raw roff), and to a lesser extent also fails criteria #2&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Another option, &lt;a href=&quot;https://github.com/rtomayko/ronn&quot; target=&quot;_blank&quot;&gt;ronn&lt;/a&gt; meets criteria #1 and #3, but it’s written in Ruby and fails #2. All of these are fine for the niches they fill, but not what I’m looking for. And as for GNU info… ugh.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://xkcd.com/912/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/nemf.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;So, after tolerating less-than-optimal tools for too long, I eventually wrote the man page generator I’d been promising for years: &lt;a href=&quot;https://git.sr.ht/~sircmpwn/scdoc&quot; target=&quot;_blank&quot;&gt;scdoc&lt;/a&gt;. In a nutshell, scdoc is a man page generator that:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Has an easy to read and write syntax. It’s inspired by Markdown, but importantly it’s not &lt;em&gt;actually&lt;/em&gt; Markdown, because Markdown is designed for HTML and not man pages.&lt;/li&gt;&lt;li&gt;Is less than 1,000 lines of POSIX.1 C99 code with no dependencies and weighs 78 KiB statically linked against musl libc.&lt;/li&gt;&lt;li&gt;Only supports generating man pages. You can post-process the roff output if you want it converted to something else (e.g. html).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I recently migrated &lt;a href=&quot;https://github.com/swaywm/sway/pull/1958&quot; target=&quot;_blank&quot;&gt;sway’s manual&lt;/a&gt; to scdoc after adding support for generating tables to it (a feature from asciidoc that the sway manual took advantage of). This change also removes a blocker to localizing man pages - something that would have been needlessly difficult to do with asciidoc. Of course, scdoc has full support for UTF-8.&lt;/p&gt;&lt;p&gt;My goal was to make a man page generator that had no more dependencies than man itself and would be a no-brainer for projects to use to make their manual more maintainable. Please give it a try!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/scdoc/</link>
        
        <pubDate>Sun, 13 May 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/scdoc/</guid>
      </item>
    
      <item>
        
        
          <title>Redirecting stderr of a running process</title>
          <description>
            &lt;p&gt;During the KDE sprint in Berlin, &lt;a href=&quot;http://www.subdiff.de/&quot; target=&quot;_blank&quot;&gt;Roman Gilg&lt;/a&gt; leaned over to me and asked if I knew how to redirect the stderr of an already-running process to a file. I Googled it and found underwhelming answers using strace and trying to decipher the output by reading the write syscalls. Instead, I thought a gdb based approach would work better, and after putting the pieces together Roman insisted I wrote a blog post on the topic.&lt;/p&gt;&lt;p&gt;gdb, the GNU debugger, has two important features that make this possible:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Attaching to running processes via &lt;code&gt;gdb -p&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Executing arbitrary code in the target process space&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With this it’s actually quite straightforward. The process is the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Attach gdb to the running process&lt;/li&gt;&lt;li&gt;Run &lt;code&gt;compile code -- dup2(open(&amp;quot;/tmp/log&amp;quot;, 65), 2)&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The magic 65 here is the value of &lt;code&gt;O_CREAT | O_WRONLY&lt;/code&gt; on Linux, which is easily found with a little program like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;sys/stat.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;fcntl.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;main&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;argc&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;argv&lt;/span&gt;) {
    &lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;%d\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;O_CREAT&lt;/span&gt; | &lt;span class=&quot;constant variable&quot;&gt;O_WRONLY&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2 is always the file descriptor assigned to stderr. What happens here is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Via &lt;a href=&quot;https://linux.die.net/man/3/open&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;open&lt;/code&gt;&lt;/a&gt;, the file you want to redirect to is created.&lt;/li&gt;&lt;li&gt;Via &lt;a href=&quot;https://linux.die.net/man/3/dup2&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;dup2&lt;/code&gt;&lt;/a&gt;, stderr is overwritten with this new file.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The &lt;code&gt;compile code&lt;/code&gt; gdb command will compile some arbitrary C code and run the result in the target process, presumably by mapping some executable RAM and loading it in, then jumping to the blob. Closing gdb (control+d) will continue the process, and it should start writing out to the file you created.&lt;/p&gt;&lt;p&gt;There are lots of other cool (and hacky) things you can do with gdb. I once disconnected someone from an internet radio by attaching gdb to nginx and closing their file descriptor, for example. Thanks to Roman for giving me the chance to write an interesting blog post on the subject!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Redirecting-stderr-of-running-process/</link>
        
        <pubDate>Fri, 04 May 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Redirecting-stderr-of-running-process/</guid>
      </item>
    
      <item>
        
        
          <title>Google embraces, extends, and extinguishes</title>
          <description>
            &lt;p&gt;Microsoft infamously coined the euphemism “&lt;a href=&quot;https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish&quot; target=&quot;_blank&quot;&gt;embrace, extend, extinguish&lt;/a&gt;” to describe their strategy for disrupting markets dominated by open standards. These days, Microsoft seems to have turned the other leaf, contributing to a huge amount of open source and supporting open standards, and is becoming a good citizen of the technology community. It’s time to turn our concerns to Google.&lt;/p&gt;&lt;p&gt;Google famously “embraced” email on April Fool’s day, 2004, which is of course based on an open standard and federates with the rest of the world. If you’ve read the news lately, you might have seen that Google is shipping a big update to GMail soon, which adds “self-destructing” emails that vanish from the recipient’s inbox after a time. Leaving aside that this promise is impossible to deliver, look at the implementation - Google emails a link to a webpage with the actual email content, and does magic in their client to make it look seamless. Thus, they “extend” email. The “extinguish” with GMail is also well underway - it’s infamous for having an extremely strict spam filter for incoming emails from people who run personal or niche mail servers.&lt;/p&gt;&lt;p&gt;Then there’s AMP. It’s an understatement to say Google embraced the web - but AMP is how they enter the “extend” phase. AMP is a “standard”, but they don’t listen to any external feedback on it and it serves as a vehicle for keeping users on their platform even when reading content from other websites. This is thought to be the main intention of the service, as there are plenty of other (and more effective) ways of rewarding lightweight pages in their search results. The “extinguish” phase comes as sites that don’t play ball get pushed out of Google search results and into obscurity. AMP is perhaps the most blatant of Google’s strategies, serving only to further Google’s agenda at the expense of everyone else.&lt;/p&gt;&lt;p&gt;The list of grievances continues. Consider Google’s dizzying collection of chat applications. In its initial form, gtalk supported XMPP, an open and federated standard for chat applications. Google dropped support for XMPP in 2014 and continued the development of their proprietary platform up thru today’s Hangouts and Google Chat platforms - neither of which support any open standards. Slack is also evidently taking cues from Google here, recently shutting down their own IRC and XMPP bridges.&lt;/p&gt;&lt;p&gt;Google Reader’s discontinuation fits too. RSS’s decline was evident before Google axed it, but killing Reader dealt a huge blow to any of RSS’s remaining momentum. Google said themselves they wanted to consolidate users onto the rest of their services - none of which, I should add, support any open syndication standards.&lt;/p&gt;&lt;p&gt;What of Google’s role as a participant in open source? Sure, they make a lot of software open source, but they don’t collaborate with anyone.  They forked from WebKit to get Apple out of the picture, and contributing to Chromium as a non-Googler is notoriously difficult. Android is the same story - open source in principle, but non-Googler AOSP contributors bemoan their awful approach to external patches. It took Google over a decade to start making headway on upstreaming their Linux patches for Android, too. Google writes papers about AI, presumably to incentivize their academics with recognition for their work. This is great until you notice that the crucial piece, the trained models, is always absent.&lt;/p&gt;&lt;p&gt;For many people, the alluring convenience of Google’s services is overwhelming. It’s hard to hear these things. But we must face facts: embrace, extend, extinguish is a core part of Google’s playbook today. It’s important that we work to diversify the internet and fight the monoculture they’re fostering.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;2018-05-04 18:12 UTC&lt;/strong&gt;: I retract my criticism of Google’s open source portfolio as a whole, and acknowledge their positive impact on many projects. However, of the projects explicitly mentioned I maintain that my criticism is valid.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2018-05-05 11:17 UTC&lt;/strong&gt;: Apparently the previous retraction caused some confusion. I am &lt;em&gt;only&lt;/em&gt; retracting the insinuation that Google isn’t a good actor in open source, namely the first sentence of paragraph 6. The rest of the article has not been retracted.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Google-embraces-extends-extinguishes/</link>
        
        <pubDate>Thu, 03 May 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Google-embraces-extends-extinguishes/</guid>
      </item>
    
      <item>
        
        
          <title>Sway reporting in from KDE&apos;s Berlin development sprint</title>
          <description>
            &lt;p&gt;I’m writing to you from an airplane on my way back to Philadelphia, after spending a week in Berlin working with the KDE team. It was great to meet those folks and work with them for a while. It’ll take me some time to get the taste of C++ out of my mouth, though! In all seriousness, it was a very productive week and I feel like we have learned a lot about each other’s projects and have a strengthened interest in collaborating more in the future.&lt;/p&gt;&lt;p&gt;The main purpose of my trip was to find opportunities for &lt;a href=&quot;http://swaywm.org&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt; and &lt;a href=&quot;http://kde.org&quot; target=&quot;_blank&quot;&gt;KDE&lt;/a&gt; to work together on improving the Linux desktop. Naturally, the main topic of discussion was interopability of software written for each of our projects. I brought the wlroots layer-shell protocol to the table seeking their feedback on it, as well as reviewing how their desktop shell works today. From our discussions we found a lot of common ground in our designs and needs, as well as room for improvement in both of our approaches.&lt;/p&gt;&lt;p&gt;The KDE approach to their desktop shell is similar to the original sway approach. Today, their Plasma shell uses a number of proprietary protocols which are hacks on top of the xdg-shell protocol (for those not in the know, the xdg-shell protocol is used to render normal desktop windows and is not designed for use with e.g. panels) that incorporate several of the concepts they were comfortable using on X11 in an almost 1:1 fashion. Sway never had any X11 concepts to get comfortable with, but some may not know that sway’s panel, wallpaper, and lock screen programs on the 0.x releases are also hacks on top of xdg-shell that are not portable between compositors.&lt;/p&gt;&lt;p&gt;In the wlroots project (which is overseen by sway), we’ve been developing a new protocol designed for desktop shell components like these. In theory, it is a more generally applicable approach to building desktop shells on Wayland than the approach we were using before. I sat down with the KDE folks and went over this protocol in great detail, and learned about how Plasma shell works today, and we were happy to discover that the wlroots approach (with some minor tweaks) should be excellently suited to Plasma shell. In addition to the layer-shell, we reviewed several other protocols Plasma uses to build its desktop experience, and identified more places where it makes sense for us to unify our approach. Other subjects discussed included virtual desktops, external window management, screen capture and pipewire, and more.&lt;/p&gt;&lt;p&gt;The upshot of this is that we believe it’s possible to integrate the Plasma shell with sway. Users of KDE on X11 were able to replace kwin with i3 and still utilize the Plasma shell - a feature which was lost in the transition to Wayland. As we continue to work together, this use-case may well be captured again. Even KDE users who are uninterested in sway stand to benefit from this. The hacks Plasma uses today are temporary and unmaintainable, and the improvements to Plasma’s codebase will make it easier to work with. Should kwin grow stable layer-shell support, clients designed for sway will work on KDE as well. Replacing sway’s own similar hacks will have similar benefits for our codebase and open the door to 3rd-party panels, lockscreens, rofi, etc.&lt;/p&gt;&lt;p&gt;I spent my time in their care working on actual code to this end. I wrote up a C++ library that extends Qt with layer-shell support called &lt;a href=&quot;https://github.com/SirCmpwn/qtlayershell&quot; target=&quot;_blank&quot;&gt;qtlayershell&lt;/a&gt;, and extended the popular &lt;a href=&quot;#&quot;&gt;Latte Dock&lt;/a&gt; KDE extension to support it. Though this work is not complete, it works - as I write this blog post, Latte is running on my sway session! This is good progress, but I must return my focus to wlroots soon. If you are interested in this work, please help me complete it!&lt;/p&gt;&lt;p&gt;A big thanks goes to KDE for putting on this event and covering my travel costs to attend. I hope they found it as productive as I did, and I’m very excited about working more with them in the future. The future of Wayland is bright!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/KDE-Sprint-retrospective/</link>
        
        <pubDate>Sat, 28 Apr 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/KDE-Sprint-retrospective/</guid>
      </item>
    
      <item>
        
        
          <title>Achtung! Decentralize, decentralize, decentralize!</title>
          <description>
            &lt;p&gt;I can hardly believe it, but the media is finally putting Facebook’s feet to the fire! No longer is it just the weird paranoid kids shouting at everyone to stop giving all of their information to these companies. We need to take this bull by the horns and drive it in a productive direction, and for that reason, it’s time to talk about decentralization, federation, and open source.&lt;/p&gt;&lt;p&gt;&lt;em&gt;This article has been &lt;a href=&quot;http://getcolorings.com/ru-decentralize&quot; target=&quot;_blank&quot;&gt;translated into Russian&lt;/a&gt; by &lt;a href=&quot;http://getcolorings.com&quot; target=&quot;_blank&quot;&gt;Get Colorings&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;It’s important to remember that Facebook is not the only villain on this stage. Did you know that Google keeps &lt;a href=&quot;https://www.google.com/maps/timeline?pb&quot; target=&quot;_blank&quot;&gt;a map of everywhere you’ve been&lt;/a&gt;? That Twitter is analyzing your tweets just like Facebook does, and sells it to advertisers just like Facebook does? Virtually all internet companies - Snapchat, Tinder, Uber &amp; Lift, and even more - are spying on you and selling it to advertisers. It’s so lucrative and easy to do this that it’s become an &lt;em&gt;industry standard practice&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;The solution to the Facebook problem is not jumping ship to another centralized commercial platform. They will be exactly the same. The commercial model for internet services is inherently flawed. Companies like Facebook, publicly traded, have a legal obligation to maximize profits for their shareholders. Private companies with investors are similarly obligated. Nowhere in the equation does it say that they’re obligated to do &lt;em&gt;anything&lt;/em&gt; for you - the only role you serve is to be a vehicle for exploitation.&lt;/p&gt;&lt;p&gt;You need to find services whose incentives are aligned with yours. What asks do you have from your social media platforms? It probably starts with basic things:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I want to keep up with my family and friends&lt;/li&gt;&lt;li&gt;I want my family and friends to be able to keep up with me&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;But if you’re smart, you might have some deeper asks:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I don’t want my personal information sold to others&lt;/li&gt;&lt;li&gt;I don’t want to be manipulated into spending my money&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We might even have some asks as a &lt;em&gt;society&lt;/em&gt;, too:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We don’t want to be manipulated into hating our countrymen&lt;/li&gt;&lt;li&gt;We don’t want to have our people’s opinions radicalized&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Each company I’ve mentioned, and many more, may offer you some subset of these promises. But &lt;em&gt;in every case&lt;/em&gt;, they will have conditions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;We’ll help you keep up with family and friends&lt;/strong&gt;, or at least the subset of them that we think makes you more profitable.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;We’ll help your family and friends keep up with you&lt;/strong&gt;, so long as your posts are engaging enough to keep them looking at our ads.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Your personal information won’t be sold to others&lt;/strong&gt;, unless we can get away with it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;You won’t be manipulated into spending your money&lt;/strong&gt;, unless we can manipulate you into spending it on us.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;We won’t manipulate you into hating your countrymen&lt;/strong&gt;, unless it makes you spend more time using our platform to express your hatred.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;We won’t radicalize your opinions&lt;/strong&gt;, at least not the ones that don’t get you angry enough to spend more time looking at our ads.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I’m not just being cynical here. There is no promise that a company can make to its users that outweighs the &lt;a href=&quot;https://legal-dictionary.thefreedictionary.com/fiduciary+duty&quot; target=&quot;_blank&quot;&gt;fiduciary duty&lt;/a&gt; that &lt;em&gt;obligates&lt;/em&gt; them to maximize profits by any means. The only defense of this is legislation and consumer choice. We must pass laws that defend users and we must choose not to engage with companies that behave like this.&lt;/p&gt;&lt;p&gt;We must do both of these things, but for now I’m going to focus on the consumer choice. We must throw our lot in with the alternative to these corporations - decentralized, federated, open source platforms.&lt;/p&gt;&lt;p&gt;What do each of these terms mean?&lt;/p&gt;&lt;p&gt;&lt;em&gt;Decentralized&lt;/em&gt; means that the platform is, well, not &lt;em&gt;centralized&lt;/em&gt;. Rather than the control being in the hands of one company (or a single interested party, to generalize it a bit), control is in the hands of many independent operators.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Federated&lt;/em&gt; refers to a means by which several service operators can communicate with each other in standard ways. This approach prevents platform lock-in. Email is a federated system - you can send an email from your gmail account to your mother’s old AOL account. Contrast this to Facebook, where you can’t follow your friend’s Twitter account.&lt;/p&gt;&lt;p&gt;Finally, &lt;em&gt;open source&lt;/em&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; is a term used by the technology community to refer to the free distribution of the secret sauce that makes our services tick. The technology engineering community collectively works on these projects and freely shares this work with everyone else.&lt;/p&gt;&lt;p&gt;The combination of all of these ideas in one piece of software is the golden ticket to internet freedom. This is the approach to social networking taken most famously by &lt;a href=&quot;http://joinmastodon.org/&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;. Mastodon is a decentralized, federated, and open source platform. The computing infrastructure the platform runs on is operated by thousands of independent volunteers (decentralized), which all communicate with each other and other software using standard protocols (federated), and the &lt;a href=&quot;https://github.com/tootsuite/mastodon&quot; target=&quot;_blank&quot;&gt;source code&lt;/a&gt; is freely available for anyone to use and improve (open source)&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;The incentives of the operators are aligned with the incentives of the users on Mastodon. The operator of each instance is a human being who can be easily reached to give feedback and thanks, rather than a billionaire egomaniac who buys an entire neighborhood so no one can bother him. Because the costs of maintaining this social network are distributed across thousands of operators, each one has a very low cost of operation, which is usually easily covered by donations from the users who they support. There are no investors to please. Just the users.&lt;/p&gt;&lt;p&gt;Mastodon fills a Twitter-like niche. There are other platforms attempting to fill other niches - &lt;a href=&quot;https://diasporafoundation.org/&quot; target=&quot;_blank&quot;&gt;diaspora*&lt;/a&gt; is easily compared to Facebook, for example. &lt;a href=&quot;https://github.com/Chocobozzz/PeerTube&quot; target=&quot;_blank&quot;&gt;PeerTube&lt;/a&gt; is under development to fulfill a YouTube-like niche, too. These platforms need our support.&lt;/p&gt;&lt;p&gt;Commercial platforms don’t respect you. You may have grown used to skimming over ads and content you don’t want to see on Facebook and other platforms. It’s an annoyance that you’ve internalized because, well, what else can you do? There are no ads on Mastodon. It doesn’t need them, and you deserve better than them.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Remember, Facebook is not the only evil. It’s time to discard proprietary platforms like the manipulative trash they are.  Take the anger you’ve felt at Facebook these past couple of weeks and use it to embrace decentralization, federation, and open source.&lt;/p&gt;&lt;p&gt;I know it seems a monumental task to untangle your life from these companies, but you don’t have to do it all at once. If this article moved you, make a todo list right now. List each way in which you’re tied to some platform - you use Facebook to talk to your friends, or use gmail for your email address, your contacts are stored on Google, you use Facebook’s calendar for social events, you have a Twitter account you haven’t moved… then take on each task one at a time. Take as much time as you need. As you research these options, if you find the open options lacking, let the people involved know what your needs are. If there’s no open option at all, please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email me&lt;/a&gt; about it.&lt;/p&gt;&lt;p&gt;We can do this. We can be free.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Decentralize-decentralize-decentralize/</link>
        
        <pubDate>Sat, 24 Mar 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Decentralize-decentralize-decentralize/</guid>
      </item>
    
      <item>
        
        
          <title>Hack everything without fear</title>
          <description>
            &lt;p&gt;We live in a golden age of open source, and it can sometimes be easy to forget the privileges that this affords us. I’m writing this article with vim, in a terminal emulator called urxvt, listening to music with mpv, in a Sway desktop session, on the Linux kernel. Supporting this are libraries like glibc or musl, harfbuzz, and mesa. I also have the support of the AMDGPU video driver, libinput and udev, alsa and pulseaudio.&lt;/p&gt;&lt;p&gt;All of this is open source. I can be reading the code for any of these tools within 30 seconds, and for many of these tools I already have their code checked out somewhere on my filesystem. It gets even better, though: these projects don’t just make their code available - they accept patches, too! Why wouldn’t we take advantage of this tremendous opportunity?&lt;/p&gt;&lt;p&gt;I often meet people who are willing to contribute to one project, but not another. Some people will shut down when they’re faced with a problem that requires them to dig into territory that they’re unfamiliar with. In Sway, for example, it’s often places like libinput or mesa. These tools might seem foreign and scary - but to these people, at some point, so did Sway. In reality these codebases are quite accessible.&lt;/p&gt;&lt;p&gt;Getting around in an unfamiliar repository can be a little intimidating, but do it enough times and it’ll become second nature. The same tools like gdb work just as well on them. If you have a stacktrace for a segfault originating in libinput, compile libinput with symbols and gdb will show you the file name and line number of the problem. Go there and read the code! Learn how to use tools like &lt;code&gt;git grep&lt;/code&gt; to find stuff. Run &lt;code&gt;git blame&lt;/code&gt; to see who wrote a confusing line of code, and send them an email! When you find the problem, don’t be afraid to send a patch over instead of working around it in your own code. This is something every programmer should be comfortable doing often.&lt;/p&gt;&lt;p&gt;Even when the leads you’re chasing down are written in unfamiliar programming languages or utilize even more unfamiliar libraries, don’t despair. All programming languages have a lot in common and huge numbers of resources are available online. Learning just enough to understand (and fix!) a particular problem is very possible, and something I find myself doing it all the time. You don’t have to be an expert in a particular programming language to invoke trial &amp; error.&lt;/p&gt;&lt;p&gt;If you’re similarly worried about the time investment, don’t be. You already set aside time to work your problem, and this is just part of that process. Yes, you’ll probably be spending your time differently from your expectations - more reading code than writing code.  But how is that any less productive? The biggest time sink in this process is all the time you spend worrying about how much time it’s going to take, or telling me in IRC you can’t solve your problem because you’re not good enough to understand mesa or the kernel or whatever.&lt;/p&gt;&lt;p&gt;An important pastime of the effective programmer is reading and understanding the tools you use. You should at least have a basic idea of how everything on your system works, and in the places your knowledge is lacking you should make it your business to study up. The more you do this, the less scary foreign code will become, and the more productive you will be. No longer will you be stuck in your tracks because your problem leads you away from the beaten path!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Hack-everything-without-fear/</link>
        
        <pubDate>Sat, 17 Mar 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Hack-everything-without-fear/</guid>
      </item>
    
      <item>
        
        
          <title>How to write an IRC bot</title>
          <description>
            &lt;p&gt;My disdain for Slack and many other Silicon Valley chat clients is &lt;a href=&quot;https://drewdevault.com/blog/Please-stop-using-slack/&quot;&gt;well known&lt;/a&gt;, as is my undying love for IRC. With Slack making the news lately after their recent decision to disable the IRC and XMPP gateways in a classic &lt;a href=&quot;https://en.wikipedia.org/wiki/Embrace%2C_extend%2C_and_extinguish&quot; target=&quot;_blank&quot;&gt;Embrace Extend Extinguish&lt;/a&gt; move, they’ve been on my mind and I feel like writing about them more. Let’s compare writing a bot for Slack with writing an IRC bot.&lt;/p&gt;&lt;p&gt;First of all, let’s summarize the process for making a Slack bot. Full details are available in &lt;a href=&quot;https://api.slack.com/slack-apps&quot; target=&quot;_blank&quot;&gt;their documentation&lt;/a&gt;. The basic steps are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a Slack account and “workspace” to host the bot (you may have already done this step). On the free plan you can have up to 10 “integrations” (aka bots). This includes all of the plug-n-play bots Slack can set up for you, so make sure you factor that into your count. Otherwise you’ll be heading to the pricing page and making a case to whoever runs your budget.&lt;/li&gt;&lt;li&gt;Create a “Slack app” through their web portal. The app will be tied to the company you work with now, and if you get fired you will lose the app. Make sure you make a separate organization if this is a concern!&lt;/li&gt;&lt;li&gt;The recommended approach from here is to set up subscriptions to the “Event API”, which involves standing up a web server (with working SSL) on a consistent IP address (and don’t forget to open up the firewall) to receive incoming notifications from Slack. You’ll need to handle a proprietary challenge to verify your messages via some HTTP requests coming from Slack which gives you info to put into HTTP headers of your outgoing requests. The Slack docs refer to this completion of this process as “triumphant success”.&lt;/li&gt;&lt;li&gt;Receive some JSON in a proprietary format via your HTTP server and use some more proprietary HTTP APIs to respond to it.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Alternatively, instead of steps 3 and 4 you can use the “Real Time Messaging” API, which is a websocket-based protocol that starts with an HTTP request to Slack’s authentication endpoint, then a follow-up HTTP request to open the WebSocket connection. Then you set up events in a similar fashion. Refer to the complicated table in the documentation breaking down which events work through which API.&lt;/p&gt;&lt;p&gt;Alright, so that’s the Slack way. How does the IRC way compare? IRC is an open standard, so to learn about it I can just read RFC 1459, which on my system is conveniently waiting to be read at &lt;code&gt;/usr/share/doc/rfc/txt/rfc1459.txt&lt;/code&gt;. This means I can just read it locally, offline, in the text editor of my choice, rather than on some annoying website that calls authentication a “triumphant success” and complains about JavaScript being disabled.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This blog post pre-dates the commercial take-over of and subsequent obsolescence of Freenode. The network this connects to, and channel it mentions, no longer exist. Running these commands will not work, though the principles remain correct.&lt;/p&gt;&lt;p&gt;You don’t have to read it right now, though. I can give you a summary here, like I gave for Slack. Let’s start by not writing a bot at all - let’s just manually throw some bits in the general direction of Freenode. Install netcat and run &lt;code&gt;nc irc.freenode.net 6667&lt;/code&gt;, then type this into your terminal:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;NICK joebloe
USER joebloe 0.0.0.0 joe :Joe Bloe
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hey, presto, you’re connected to IRC! Type this in to join a channel:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;JOIN #cmpwn
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then type this to say hello:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;PRIVMSG #cmpwn :Hi SirCmpwn, I&amp;apos;m here from your blog!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;IRC is one of the simplest protocols out there, and it’s dead easy to write a bot for it. If your programming language can open a TCP socket (it can), then you can use it to write an IRC bot in 2 minutes, flat. That’s not even to mention that there are IRC client libraries available for every programming language on every platform ever - I even &lt;a href=&quot;https://github.com/SirCmpwn/ChatSharp&quot; target=&quot;_blank&quot;&gt;wrote one myself!&lt;/a&gt; In fact, that guy is probably the fifth or sixth IRC library I’ve written. They’re so easy to write that I’ve lost count.&lt;/p&gt;&lt;p&gt;Slack is a walled garden. Their proprietary API is defined by them and only implemented by them. They can and will shut off parts you depend on (like the IRC+XMPP gateways that were just shut down). IRC is over 20 years old and software written for it then still works now. It’s implemented by hundreds of clients, servers, and bots. Your CI supports it and GitHub can send commit notifications to it. It’s ubiquitous and free. Use it!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-write-an-IRC-bot/</link>
        
        <pubDate>Sat, 10 Mar 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-write-an-IRC-bot/</guid>
      </item>
    
      <item>
        
        
          <title>The path to sustainably working on FOSS full-time</title>
          <description>
            &lt;p&gt;This is an article I didn’t think I’d be writing any time soon. I’ve aspired to work full-time on my free and open source software projects for a long time now, but I have never expected that it could work. However, as of this week, I finally have enough recurring donation revenue to break even on FOSS, and I’ve started to put the extra cash away. I needed to set the next donation goal and ran the numbers to figure out what it takes to work on FOSS full-time.&lt;/p&gt;&lt;p&gt;Let me start with some context. I like to say “one-time donations buy pizza, but recurring donations buy sustainable FOSS development”. One-time donations provide no financial security, so to date, (almost) all of my FOSS work has been done in my spare time, and I’ve had to spend most of my time working on proprietary software to make a living. This is the case for many other free software advocates as well. Short of large grants on the scale of several tens of thousands of dollars, if you want to get your rent paid and put food on the table you need to be able to rely on something consistent.&lt;/p&gt;&lt;p&gt;Some projects (e.g. Docker, Gitlab) have a compelling product in the market and can build a company around their open source product. Some projects fulfill a tangible need for some other business (such as writing software they depend on), and for these projects large corporate sponsorships are often possible. However, other kinds of projects (including most of my own) often have to rely on their users for donations, and this has traditionally been a pretty dubious prospect. In August of 2017, I was making $0 per month in recurring donations to &lt;a href=&quot;https://drewdevault.com/donate&quot; target=&quot;_blank&quot;&gt;fosspay&lt;/a&gt;, down from an all-time peak of $20 per month. When I was researching the possibility of starting a Patreon campaign, the norm was less than $50/month even for the most successful open source campaigns. As you can imagine, I was somewhat pessimistic.&lt;/p&gt;&lt;p&gt;To my happy surprise, recurring donations to open source projects have taken off, both for me and many others. It’s amazing. After years of failing to earn a substantial income from open source, as of today I’m receiving $547.74 per month from three donation platforms (&lt;a href=&quot;https://drewdevault.com/donate&quot; target=&quot;_blank&quot;&gt;fosspay&lt;/a&gt;, &lt;a href=&quot;https://liberapay.com/SirCmpwn&quot; target=&quot;_blank&quot;&gt;LiberaPay&lt;/a&gt;, and &lt;a href=&quot;https://patreon.com/SirCmpwn&quot; target=&quot;_blank&quot;&gt;Patreon&lt;/a&gt;). What’s amazing is that because the income comes from from several platforms and is distributed across over 80 donators, I can feel confident in the security of this model. There are no whales whose donations I have to live in fear of losing. There is no single platform that I have to worry about going under or dramatically &lt;a href=&quot;https://blog.patreon.com/not-rolling-out-fees-change/&quot; target=&quot;_blank&quot;&gt;changing their fee structure&lt;/a&gt;. This is unprecedented - we’re truly seeing the age of user-supported FOSS begin.&lt;/p&gt;&lt;p&gt;I want to provide some transparency on how I set my goals and where the money goes. You might be surprised to have heard me say that I’m only “breaking even” on open source at $500/month! Many projects can run on a leaner budget, but because I maintain so many different projects, I have different infrastructure requirements. This mainly includes domains and servers for CI, project hosting, releases, etc. At my scale, it’s most cost-effective for me to self-host my own dedicated servers in a local datacenter here in Philadelphia. This costs me $380/month at the moment for 5U including power and network. I’m not done moving my legacy infrastructure into the new datacenter, though, so I’m still paying for some virtual private servers. As I migrate these, I will be reinvesting the money saved into upgrading the new infrastructure.&lt;/p&gt;&lt;p&gt;The next question is where to go from here. I have set my full-time goal at $6,000 per month, which works out to $72,000 per year pre-tax, pre-infrastructure expenses. This number is a lofty goal, and one that I expect won’t be met for a long time, if at all. This number is based on several factors: cost of living, financial security, and taxes. The number is a significant decrease from what I earn today, but it is enough to meet each of these criteria. Let’s break it down.&lt;/p&gt;&lt;p&gt;Right now, I live in a pretty nice apartment in center city Philadelphia, which costs me about $1700 per month. There are cheaper areas, but I make a comfortable salary at my current job, which allows me to buy a nicer place. If working on FOSS full-time appears viable, I will move to a cheaper location when my lease is up and adjust the goal accordingly (I will probably move to a cheaper location when my lease is up regardless, actually). Because I’m locked into my lease (among other reasons), I did not factor major lifestyle changes like moving to a cheaper location into the goal. Other costs of living, such as food and necessities, work out to about $1000 per month.&lt;/p&gt;&lt;p&gt;The other concern is financial security. I am lucky to live a comfortable life today, but that is a result of hard lessons learned and has not always been the case. I cannot focus on FOSS if I’m only earning just enough to cover my expenses. Any major change in my life circumstances, such as a medical emergency, natural disaster, or even something as benign as my computer breaking down, would be a serious problem. Therefore, for me to consider working full-time on anything, the earnings have to allow me to save money. To this end, my earnings floor is at least 1.5x my expenditures. Some people think a more liberal ratio is fine, but I’m a bit more conservative - I used to really struggle to make ends meet. This raises the total to around $4000 per month.&lt;/p&gt;&lt;p&gt;Add to this infrastructure costs we already talked about, and the total becomes $4500 per month. Now we have to consider tax. If we look up the current &lt;a href=&quot;https://en.wikipedia.org/wiki/Tax_bracket#2018_tax_brackets_under_current_law&quot; target=&quot;_blank&quot;&gt;tax brackets in the United States&lt;/a&gt; and do some guesswork, we can estimate that I’ll land in the 22% bracket under this model. If I need my take-home to be $4500, we can divide that by 78% and arrive at the total: $5769 per month&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Round it up to $6000 and this is our goal.&lt;/p&gt;&lt;p&gt;These numbers are pretty high. I understand many people, including some of those who donate to me, are much less fortunate than I. My lifestyle is a reflection of my assumption that the open source donation model does not provide a sustainable source of income. Based on this, I’ve focused my career on paid proprietary software development, which pays very competitively in the United States. The privileges afforded by this have shaped my costs of living. Rather than make up a number smaller than my actual expenditures, I prefer to be honest with you about this.&lt;/p&gt;&lt;p&gt;This doesn’t necessarily have to remain the case forever. As my income from donations increase, utilizing them as a primary source of income becomes more feasible, and I am prepared to reorient my life with this in mind. You can expect my donation goal to &lt;em&gt;decrease&lt;/em&gt; as the number of donations &lt;em&gt;increases&lt;/em&gt;. This will probably take a long time, on the scale of years. My housing situation and costs of living in Philadelphia will change during this time - I might not stay in Philadelphia, I might have to change jobs, etc. It’s difficult to set a more optimistic goal today that will prove correct when its met. For that reason, my goal is adjusted with respect to my current conditions, not the ideal.&lt;/p&gt;&lt;p&gt;So that’s how it shakes out! I’m glad we can finally have this conversation, and I’m incredibly thankful for your support. Thank you for everything, and I’m looking forward to making even more cool stuff for you in the future.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-road-to-sustainable-FOSS/</link>
        
        <pubDate>Sat, 24 Feb 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-road-to-sustainable-FOSS/</guid>
      </item>
    
      <item>
        
        
          <title>Writing a Wayland Compositor, Part 2: Rigging up the server</title>
          <description>
            &lt;p&gt;This is the second in a series of articles on the subject of writing a Wayland compositor from scratch using &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;. Check out &lt;a href=&quot;https://drewdevault.com/blog/Writing-a-Wayland-compositor-1/&quot;&gt;the first article&lt;/a&gt; if you haven’t already. Last time, we ended up with an application which fired up a wlroots backend, enumerated output devices, and drew some pretty colors on the screen. Today, we’re going to start accepting Wayland client connections, though we aren’t going to be doing much with them yet.&lt;/p&gt;&lt;p&gt;The commit that this article dissects is &lt;a href=&quot;https://github.com/SirCmpwn/mcwayland/commit/b45c651&quot; target=&quot;_blank&quot;&gt;b45c651&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;A quick aside on the nature of these blog posts: it’s going to take &lt;em&gt;a lot&lt;/em&gt; of these articles to flesh out our compositor. I’m going to be publishing these more frequently than usual, probably 1-2 per week, and continue posting my usual articles at the typical rate. Okay? Cool.&lt;/p&gt;&lt;p&gt;So we’ve started up the backend and we’re rendering something interesting, but we still aren’t running a Wayland server – Wayland clients aren’t connecting to our application. Adding this is actually quite easy:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;attribute&quot;&gt;@@ -113,12 +113,18 @@ int main(int argc, char **argv) {&lt;/span&gt;
        server.new_output.notify = new_output_notify;
        wl_signal_add(&amp;amp;server.backend-&amp;gt;events.new_output, &amp;amp;server.new_output);
 
&lt;span class=&quot;string&quot;&gt;+       const char *socket = wl_display_add_socket_auto(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       assert(socket);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
        if (!wlr_backend_start(server.backend)) {
                fprintf(stderr, &amp;quot;Failed to start backend\n&amp;quot;);
                wl_display_destroy(server.wl_display);
                return 1;
        }
 
&lt;span class=&quot;string&quot;&gt;+       printf(&amp;quot;Running compositor on wayland display &amp;apos;%s&amp;apos;\n&amp;quot;, socket);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       setenv(&amp;quot;WAYLAND_DISPLAY&amp;quot;, socket, true);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
        wl_display_run(server.wl_display);
        wl_display_destroy(server.wl_display);
        return 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it! If you run McWayface again, it’ll print something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Running compositor on wayland display &amp;apos;wayland-1&amp;apos;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://cgit.freedesktop.org/wayland/weston/&quot; target=&quot;_blank&quot;&gt;Weston&lt;/a&gt;, the Wayland reference compositor, includes a number of simple reference clients. We can use &lt;code&gt;weston-info&lt;/code&gt; to connect to our server and list the &lt;strong&gt;globals&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ WAYLAND_DISPLAY=wayland-1 weston-info
interface: &amp;apos;wl_drm&amp;apos;, version: 2, name: 1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you recall from my &lt;a href=&quot;https://drewdevault.com/blog/Introduction-to-Wayland/&quot;&gt;Introduction to Wayland&lt;/a&gt;, the Wayland server exports a list of &lt;strong&gt;globals&lt;/strong&gt; to clients via the Wayland registry. These globals provide interfaces the client can utilize to interact with the server. We get &lt;code&gt;wl_drm&lt;/code&gt; for free with wlroots, but we have not actually wired up anything useful yet. Wlroots provides many “types”, of which the majority are implementations of Wayland global interfaces like this.&lt;/p&gt;&lt;p&gt;Some of the wlroots implementations require some rigging from you, but several of them just take care of themselves. Rigging these up is easy:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;        printf(&amp;quot;Running compositor on wayland display &amp;apos;%s&amp;apos;\n&amp;quot;, socket);
        setenv(&amp;quot;WAYLAND_DISPLAY&amp;quot;, socket, true);
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wl_display_init_shm(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_gamma_control_manager_create(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_screenshooter_create(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_primary_selection_device_manager_create(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_idle_create(server.wl_display);&lt;/span&gt;
 
        wl_display_run(server.wl_display);
        wl_display_destroy(server.wl_display);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that some of these interfaces are not necessarily ones that you typically would want to expose to all Wayland clients - screenshooter, for example, is something that should be secured. We’ll get to security in a later article. For now, if we run &lt;code&gt;weston-info&lt;/code&gt; again, we’ll see a few more globals have appeared:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ WAYLAND_DISPLAY=wayland-1 weston-info
interface: &amp;apos;wl_shm&amp;apos;, version: 1, name: 3
	formats: XRGB8888 ARGB8888
interface: &amp;apos;wl_drm&amp;apos;, version: 2, name: 1
interface: &amp;apos;gamma_control_manager&amp;apos;, version: 1, name: 2
interface: &amp;apos;orbital_screenshooter&amp;apos;, version: 1, name: 3
interface: &amp;apos;gtk_primary_selection_device_manager&amp;apos;, version: 1, name: 4
interface: &amp;apos;org_kde_kwin_idle&amp;apos;, version: 1, name: 5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You’ll find that wlroots implements a variety of protocols from a variety of sources - here we see protocols from Orbital, GTK, and KDE represented. Wlroots includes an example client for the orbital screenshooter - we can use it now to take a screenshot of our compositor:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ WAYLAND_DISPLAY=wayland-1 ./examples/screenshot
cannot set buffer size
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ah, this is a problem - you may have noticed that we don’t have any wl_output globals, which the screenshooter client relies on to figure out the resolution of the screenshot buffer. We can add these, too:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;attribute&quot;&gt;@@ -95,6 +99,8 @@ static void new_output_notify(struct wl_listener *listener, void *data) {&lt;/span&gt;
        wl_signal_add(&amp;amp;wlr_output-&amp;gt;events.destroy, &amp;amp;output-&amp;gt;destroy);
        output-&amp;gt;frame.notify = output_frame_notify;
        wl_signal_add(&amp;amp;wlr_output-&amp;gt;events.frame, &amp;amp;output-&amp;gt;frame);
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_output_create_global(wlr_output);&lt;/span&gt;
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;weston-info&lt;/code&gt; again will give us some info about our outputs now:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ WAYLAND_DISPLAY=wayland-1 weston-info
interface: &amp;apos;wl_drm&amp;apos;, version: 2, name: 1
interface: &amp;apos;wl_output&amp;apos;, version: 3, name: 2
	x: 0, y: 0, scale: 1,
	physical_width: 0 mm, physical_height: 0 mm,
	make: &amp;apos;wayland&amp;apos;, model: &amp;apos;wayland&amp;apos;,
	subpixel_orientation: unknown, output_transform: normal,
	mode:
		width: 952 px, height: 521 px, refresh: 0.000 Hz,
		flags: current
interface: &amp;apos;wl_shm&amp;apos;, version: 1, name: 3
	formats: XRGB8888 ARGB8888
interface: &amp;apos;gamma_control_manager&amp;apos;, version: 1, name: 4
interface: &amp;apos;orbital_screenshooter&amp;apos;, version: 1, name: 5
interface: &amp;apos;gtk_primary_selection_device_manager&amp;apos;, version: 1, name: 6
interface: &amp;apos;org_kde_kwin_idle&amp;apos;, version: 1, name: 7
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can take that screenshot! Give it a shot (heh)!&lt;/p&gt;&lt;p&gt;We’re getting close to the good stuff now. The next article is going to introduce the concept of &lt;strong&gt;surfaces&lt;/strong&gt;, and we will use them to render our first window. If you had any trouble with this article, please reach out to me at &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;drew@ddevault.org&lt;/a&gt; or to the wlroots team at &lt;a href=&quot;http://webchat.freenode.net/?channels=sway-devel&amp;uio=d4&quot; target=&quot;_blank&quot;&gt;#sway-devel&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Writing-a-wayland-compositor-part-2/</link>
        
        <pubDate>Thu, 22 Feb 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Writing-a-wayland-compositor-part-2/</guid>
      </item>
    
      <item>
        
        
          <title>Writing a Wayland Compositor, Part 1: Hello wlroots</title>
          <description>
            &lt;p&gt;This is the first in a series of &lt;em&gt;many&lt;/em&gt; articles I’m writing on the subject of building a functional Wayland compositor from scratch. As you may know, I am the lead maintainer of &lt;a href=&quot;https://github.com/swaywm/sway&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt;, a reasonably popular Wayland compositor. Along with many other talented developers, we’ve been working on &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt; over the past few months. This is a powerful tool for creating new Wayland compositors, but it is very dense and difficult to understand. Do not despair! The intention of these articles is to make you understand and feel comfortable using it.&lt;/p&gt;&lt;p&gt;Before we dive in, a quick note: the wlroots team is starting a crowdfunding campaign today to fund travel for each of our core contributors to meet in person and work for two weeks on a hackathon. Please consider contributing to &lt;a href=&quot;https://www.indiegogo.com/projects/sway-hackathon-software/x/1059863&quot; target=&quot;_blank&quot;&gt;the campaign&lt;/a&gt;!&lt;/p&gt;&lt;p&gt;You &lt;strong&gt;must&lt;/strong&gt; read and comprehend my earlier article, &lt;a href=&quot;https://drewdevault.com/blog/Introduction-to-Wayland/&quot;&gt;An introduction to Wayland&lt;/a&gt;, before attempting to understand this series of blog posts, as I will be relying on concepts and terminology introduced there to speed things up. Some background in OpenGL is helpful, but not required. A good understanding of C is mandatory. If you have any questions about any of the articles in this series, please reach out to me directly via &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;drew@ddevault.org&lt;/a&gt; or to the wlroots team at &lt;a href=&quot;http://webchat.freenode.net/?channels=sway-devel&amp;uio=d4&quot; target=&quot;_blank&quot;&gt;#sway-devel on irc.freenode.net&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;During this series of articles, the compositor we’re building will live on GitHub: &lt;a href=&quot;https://github.com/SirCmpwn/mcwayface&quot; target=&quot;_blank&quot;&gt;Wayland McWayface&lt;/a&gt;. Each article in this series will be presented as a breakdown of a single commit between zero and a fully functional Wayland compositor. The commit for this article is &lt;a href=&quot;https://github.com/SirCmpwn/mcwayland/commit/f89092e&quot; target=&quot;_blank&quot;&gt;f89092e&lt;/a&gt;. I’m only going to explain the important parts - I suggest you review the entire commit separately.&lt;/p&gt;&lt;p&gt;Let’s get started. First, I’m going to define a struct for holding our compositor’s state:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;string&quot;&gt;+struct mcw_server {&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wl_display *wl_display;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wl_event_loop *wl_event_loop;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: mcw is short for McWayface. We’ll be using this acronym throughout the article series. We’ll set one of these aside and initialize the Wayland display for it&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt; int main(int argc, char **argv) {
&lt;span class=&quot;string&quot;&gt;+       struct mcw_server server;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       server.wl_display = wl_display_create();&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       assert(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       server.wl_event_loop = wl_display_get_event_loop(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       assert(server.wl_event_loop);&lt;/span&gt;
        return 0;
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Wayland display gives us a number of things, but for now all we care about is the event loop. This event loop is deeply integrated into wlroots, and is used for things like dispatching signals across the application, being notified when data is available on various file descriptors, and so on.&lt;/p&gt;&lt;p&gt;Next, we need to create the backend:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt; struct mcw_server {
        struct wl_display *wl_display;
        struct wl_event_loop *wl_event_loop;
 
&lt;span class=&quot;string&quot;&gt;+       struct wlr_backend *backend;&lt;/span&gt;
 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;backend&lt;/strong&gt; is our first wlroots concept. The backend is responsible for abstracting the low level &lt;em&gt;input&lt;/em&gt; and &lt;em&gt;output&lt;/em&gt; implementations from you. Each backend can generate zero or more input devices (such as mice, keyboards, etc) and zero or more output devices (such as monitors on your desk). Backends have nothing to do with Wayland - their purpose is to help you with the &lt;em&gt;other&lt;/em&gt; APIs you need to use as a Wayland compositor. There are various backends with various purposes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The &lt;strong&gt;drm&lt;/strong&gt; backend utilizes the Linux DRM subsystem to render directly to your physical displays.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;libinput&lt;/strong&gt; backend utilizes libinput to enumerate and control physical input devices.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;wayland&lt;/strong&gt; backend creates “outputs” as windows on another running Wayland compositors, allowing you to nest compositors. Useful for debugging.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;x11&lt;/strong&gt; backend is similar to the Wayland backend, but opens an x11 window on an x11 server rather than a Wayland window on a Wayland server.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Another important backend is the &lt;strong&gt;multi&lt;/strong&gt; backend, which allows you to initialize several backends at once and aggregate their input and output devices. This is necessary, for example, to utilize both drm and libinput simultaneously.&lt;/p&gt;&lt;p&gt;wlroots provides a helper function for automatically choosing the most appropriate backend based on the user’s environment:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;        server.wl_event_loop = wl_display_get_event_loop(server.wl_display);
        assert(server.wl_event_loop);
 
&lt;span class=&quot;string&quot;&gt;+       server.backend = wlr_backend_autocreate(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       assert(server.backend);&lt;/span&gt;
        return 0;
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I would generally suggest using either the Wayland or X11 backends during development, especially before we have a way of exiting the compositor. If you call &lt;code&gt;wlr_backend_autocreate&lt;/code&gt; from a running Wayland or X11 session, the respective backends will be automatically chosen.&lt;/p&gt;&lt;p&gt;We can now start the backend and enter the Wayland event loop:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;string&quot;&gt;+       if (!wlr_backend_start(server.backend)) {&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+               fprintf(stderr, &amp;quot;Failed to start backend\n&amp;quot;);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+               wl_display_destroy(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+               return 1;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       }&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wl_display_run(server.wl_display);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wl_display_destroy(server.wl_display);&lt;/span&gt;
        return 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run your compositor at this point, you should see the backend start up and… do nothing. It’ll open a window if you run from a running Wayland or X11 server. If you run it on DRM, it’ll probably do very little and you won’t even be able to switch to another TTY to kill it.&lt;/p&gt;&lt;p&gt;In order to render something, we need to know about the outputs we can render on. The backend provides a &lt;strong&gt;wl_signal&lt;/strong&gt; that notifies us when it gets a new output. This will happen on startup and as any outputs are hotplugged at runtime.&lt;/p&gt;&lt;p&gt;Let’s add this to our server struct:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt; struct mcw_server {
        struct wl_display *wl_display;
        struct wl_event_loop *wl_event_loop;
 
        struct wlr_backend *backend;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wl_listener new_output;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wl_list outputs; // mcw_output::link&lt;/span&gt;
 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This adds a &lt;code&gt;wl_listeners&lt;/code&gt; which is signalled when new outputs are added. We also add a &lt;code&gt;wl_list&lt;/code&gt; (which is just a linked list provided by libwayland-server) which we’ll later store some state in. To be notified, we must use &lt;code&gt;wl_signal_add&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;        assert(server.backend);
 
&lt;span class=&quot;string&quot;&gt;+       wl_list_init(&amp;amp;server.outputs);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       server.new_output.notify = new_output_notify;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wl_signal_add(&amp;amp;server.backend-&amp;gt;events.new_output, &amp;amp;server.new_output);&lt;/span&gt;
 
        if (!wlr_backend_start(server.backend)) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We specify here the function to be notified, &lt;code&gt;new_output_notify&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;string&quot;&gt;+static void new_output_notify(struct wl_listener *listener, void *data) {&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct mcw_server *server = wl_container_of(&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+                       listener, server, new_output);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wlr_output *wlr_output = data;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       if (!wl_list_empty(&amp;amp;wlr_output-&amp;gt;modes)) {&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+               struct wlr_output_mode *mode =&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+                       wl_container_of(wlr_output-&amp;gt;modes.prev, mode, link);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+               wlr_output_set_mode(wlr_output, mode);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       }&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct mcw_output *output = calloc(1, sizeof(struct mcw_output));&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       clock_gettime(CLOCK_MONOTONIC, &amp;amp;output-&amp;gt;last_frame);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       output-&amp;gt;server = server;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       output-&amp;gt;wlr_output = wlr_output;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wl_list_insert(&amp;amp;server-&amp;gt;outputs, &amp;amp;output-&amp;gt;link);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a little bit complicated! This function has several roles when dealing with the incoming &lt;code&gt;wlr_output&lt;/code&gt;. When the signal is raised, a pointer to the listener that was signaled is passed in, as well as the &lt;code&gt;wlr_output&lt;/code&gt; which was created. &lt;code&gt;wl_container_of&lt;/code&gt; uses some &lt;code&gt;offsetof&lt;/code&gt;-based magic to get the &lt;code&gt;mcw_server&lt;/code&gt; reference from the listener pointer, and we cast &lt;code&gt;data&lt;/code&gt; to the actual type, &lt;code&gt;wlr_output&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The next thing we have to do is set the &lt;strong&gt;output mode&lt;/strong&gt;. Some backends (notably x11 and Wayland) do not support modes, but they are necessary for DRM. Output modes specify a size and refresh rate supported by the output, such as &lt;code&gt;1920x1080@60Hz&lt;/code&gt;. The body of this if statement just chooses the last one (which is usually the highest resolution and refresh rate) and applies it to the output with &lt;code&gt;wlr_output_set_mode&lt;/code&gt;. We &lt;em&gt;must&lt;/em&gt; set the output mode in order to render to it.&lt;/p&gt;&lt;p&gt;Then, we set up some state for us to keep track of this output with in our compositor. I added this struct definition at the top of the file:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;string&quot;&gt;+struct mcw_output {&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wlr_output *wlr_output;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct mcw_server *server;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct timespec last_frame;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wl_list link;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will be the structure we use to store any state we have for this output that is specific to our compositor’s needs. We include a reference to the &lt;code&gt;wlr_output&lt;/code&gt;, a reference to the &lt;code&gt;mcw_server&lt;/code&gt; that owns this output, and the time of the last frame, which will be useful later. We also set aside a &lt;code&gt;wl_list&lt;/code&gt;, which is used by libwayland for linked lists.&lt;/p&gt;&lt;p&gt;Finally, we add this output to the server’s list of outputs.&lt;/p&gt;&lt;p&gt;We could use this now, but it would leak memory. We also need to handle output &lt;em&gt;removal&lt;/em&gt;, with a signal provided by wlr_output. We add the listener to the mcw_output struct:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt; struct mcw_output {
        struct wlr_output *wlr_output;
        struct mcw_server *server;
        struct timespec last_frame;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wl_listener destroy;&lt;/span&gt;
 
        struct wl_list link;
 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we hook it up when the output is added:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;         wl_list_insert(&amp;amp;server-&amp;gt;outputs, &amp;amp;output-&amp;gt;link);

&lt;span class=&quot;string&quot;&gt;+        output-&amp;gt;destroy.notify = output_destroy_notify;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+        wl_signal_add(&amp;amp;wlr_output-&amp;gt;events.destroy, &amp;amp;output-&amp;gt;destroy);&lt;/span&gt;
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will call our output_destroy_notify function to handle cleanup when the output is unplugged or otherwise removed from wlroots. Our handler looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;string&quot;&gt;+static void output_destroy_notify(struct wl_listener *listener, void *data) {&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+        struct mcw_output *output = wl_container_of(listener, output, destroy);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+        wl_list_remove(&amp;amp;output-&amp;gt;link);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+        wl_list_remove(&amp;amp;output-&amp;gt;destroy.link);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+        wl_list_remove(&amp;amp;output-&amp;gt;frame.link);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+        free(output);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one should be pretty self-explanatory.&lt;/p&gt;&lt;p&gt;So, we now have a reference to the output. However, we are still not rendering anything - if you run the compositor again you’ll notice the same behavior. In order to render things, we have to listen for the &lt;strong&gt;frame signal&lt;/strong&gt;. Depending on the selected mode, the output can only receive new frames at a certain rate. We keep track of this for you in wlroots, and emit the frame signal when it’s time to draw a new frame.&lt;/p&gt;&lt;p&gt;Let’s add a listener to the &lt;code&gt;mcw_output&lt;/code&gt; struct for this purpose:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt; struct mcw_output {
        struct wlr_output *wlr_output;
        struct mcw_server *server;
 
        struct wl_listener destroy;
&lt;span class=&quot;string&quot;&gt;+       struct wl_listener frame;&lt;/span&gt;
 
        struct wl_list link;
 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then extend &lt;code&gt;new_output_notify&lt;/code&gt; to register the listener to the frame signal:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;        output-&amp;gt;destroy.notify = output_destroy_notify;
        wl_signal_add(&amp;amp;wlr_output-&amp;gt;events.destroy, &amp;amp;output-&amp;gt;destroy);
&lt;span class=&quot;string&quot;&gt;+       output-&amp;gt;frame.notify = output_frame_notify;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wl_signal_add(&amp;amp;wlr_output-&amp;gt;events.frame, &amp;amp;output-&amp;gt;frame);&lt;/span&gt;
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, whenever an output is ready for a new frame, &lt;code&gt;output_frame_notify&lt;/code&gt; will be called. We still need to write this function, though. Let’s start with the basics:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;string&quot;&gt;+static void output_frame_notify(struct wl_listener *listener, void *data) {&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct mcw_output *output = wl_container_of(listener, output, frame);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       struct wlr_output *wlr_output = data;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to render anything here, we need to first obtain a wlr_renderer&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. We can obtain one from the backend:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt; static void output_frame_notify(struct wl_listener *listener, void *data) {
        struct mcw_output *output = wl_container_of(listener, output, frame);
        struct wlr_output *wlr_output = data;
&lt;span class=&quot;string&quot;&gt;+       struct wlr_renderer *renderer = wlr_backend_get_renderer(&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+                       wlr_output-&amp;gt;backend);&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now take advantage of this renderer to draw something on the output.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt; static void output_frame_notify(struct wl_listener *listener, void *data) {
        struct mcw_output *output = wl_container_of(listener, output, frame);
        struct wlr_output *wlr_output = data;
        struct wlr_renderer *renderer = wlr_backend_get_renderer(
                        wlr_output-&amp;gt;backend);
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_output_make_current(wlr_output, NULL);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_renderer_begin(renderer, wlr_output);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       float color[4] = {1.0, 0, 0, 1.0};&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_renderer_clear(renderer, color);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_output_swap_buffers(wlr_output, NULL, NULL);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+       wlr_renderer_end(renderer);&lt;/span&gt;
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling &lt;code&gt;wlr_output_make_current&lt;/code&gt; makes the output’s OpenGL context “current”, and from here you can use OpenGL calls to render to the output’s buffer. We call &lt;code&gt;wlr_renderer_begin&lt;/code&gt; to configure some sane OpenGL defaults for us&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;At this point we can start rendering. We’ll expand more on what you can do with &lt;code&gt;wlr_renderer&lt;/code&gt; later, but for now we’ll be satisified with clearing the output to a solid red color.&lt;/p&gt;&lt;p&gt;When we’re done rendering, we call &lt;code&gt;wlr_output_swap_buffers&lt;/code&gt; to swap the output’s front and back buffers, committing what we’ve rendered to the actual screen. We call &lt;code&gt;wlr_renderer_end&lt;/code&gt; to clean up the OpenGL context and we’re done. Running our compositor now should show you a solid red screen!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;This concludes today’s article. If you take a look at &lt;a href=&quot;https://github.com/SirCmpwn/mcwayland/commit/f89092e&quot; target=&quot;_blank&quot;&gt;the commit&lt;/a&gt; that this article describes, you’ll see that I took it a little further with some code that clears the display to a different color every frame. Feel free to experiment with similar changes!&lt;/p&gt;&lt;p&gt;Over the next two articles, we’ll finish wiring up the Wayland server and render a Wayland client on screen. Please look forward to it!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Writing-a-Wayland-compositor-1/</link>
        
        <pubDate>Sat, 17 Feb 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Writing-a-Wayland-compositor-1/</guid>
      </item>
    
      <item>
        
        
          <title>The last years</title>
          <description>
            &lt;p&gt;&lt;strong&gt;August 14th, 2019&lt;/strong&gt; PYONGYANG IN CHAOS AS PANDEMIC DECIMATES LEADERSHIP. Sources within the country have reported that a fast-acting and deadly infectious disease has suddenly infected the population of Pyongyang, the capital city of North Korea, where most of the country’s political elite live. Unconfirmed reports suggest that a significant fraction of the leadership has been affected.&lt;/p&gt;&lt;p&gt;The reclusive country has appealed for immediate aid from the international community and it is reported that a group of medical experts from Seoul have been permitted to enter via the Joint Security Area. Representatives from the United States Center for Disease Control and the Chinese Center for Disease Control and Prevention have also agreed to send representatives into the country to help control the outbreak.&lt;/p&gt;&lt;p&gt;North Korea is known for it’s unwillingness to cooperate with the international community, particularly with respect to…&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;October 7th, 2019&lt;/strong&gt; NEW APPROACH SHOWS PROMISING RESULTS FOR CYSTIC FIBROSIS. Researchers announced yesterday that they were able to design a disease which corrects the genome of patients suffering from the early stages of cystic fibrosis. The study was shown to stop the progression of the genetic disease in all subjects, and several subjects even showed signs of reversal. The FDA has begun the process of evaluating the treatment for the general public.&lt;/p&gt;&lt;p&gt;Scientists involved explained the process involved using a modified version of the common cold. They were able to reduce the negative effects of the virus, and utilized it as a means of delivering a CRISPR-based payload that directly edited the genome of members of the study. Scientists on the study suggest that in the future, a similarly benign virus could be introduced to the general public to eliminate the disease across the entire human population.&lt;/p&gt;&lt;p&gt;Some scientists are skeptical of the risks of this approach, but others spoke favorably…&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;September 30th, 2019&lt;/strong&gt; UNITED STATES CLAIMS RESPONSIBILITY FOR PYONGYANG EPIDEMIC. In response to increasing alarm in the international community regarding the origins of the artificial virus that took the life of Kim Jong-un in August, the United States government has stepped forward to claim responsibility. President Trump justified the move in a public statement, claiming that the development of North Korean nuclear weapons capable of striking American targets required such a response, and points to the ongoing reunification efforts as evidence of a job well done.&lt;/p&gt;&lt;p&gt;Many leaders of the international community have issued statements condemning the United State’s attack, though some leaders have expressed relief that the speculation regarding a rogue group of biologists was dispelled. Korean officials have also issued statements condemning the attack, noting that several presumably innocent family members of Pyongyang officials were killed, but reaffirmed their commitment to supporting the population of the North and continuing to peacefully unify the peninsula.&lt;/p&gt;&lt;p&gt;The relative ease of the reunification effort, long thought to be impossible, is the result of the incredibly swift and precise nature of the American attack…&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;November 18th, 2020&lt;/strong&gt; BRITAIN TARGET OF BIOLOGICAL ATTACK? Members of the British public have come down with a highly contagious but largely benign form of the measles, igniting panic among the population. The royal family and members of the parliament have been quarantined and the country’s biologists are examining specimens of the disease for signs of human tampering. This is the next in a series of scares, following the flu outbreak in Mexico this June.&lt;/p&gt;&lt;p&gt;We spoke with an expert in the field (who wished to remain anonymous) to understand exactly how biologically engineered diseases are possible. Our expert pointed to recent advances in genetic engineering, particularly CRISPR, which have allowed research in this field to advance at an unprecedented pace for a fraction of the costs previously associated with such research. For a layman’s explanation of what CRISPR is and how it works, see page 3.&lt;/p&gt;&lt;p&gt;Officials in Britain have issued a statement encouraging the public not to worry, and stated that they had no reason to believe…&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;February 2nd, 2021&lt;/strong&gt; LARGE GENETIC DATABASE LEAKED IN HACK. Personal genomics company 23andMe released a statement today admitting that their database of personal genetic records was leaked in a hack in May of last year. The company, founded in 2006, collects genetic records from customers curious in their ancestry and sends them a report of interesting information. The database is said to contain names, email addresses, and samples of each customer’s genome dating back to the company’s inception.&lt;/p&gt;&lt;p&gt;Estimates show that up to 3 million customers are affected, mostly from the United States. The company has not revealed how much of each customer’s genome was disclosed, but experts agree that it would not have been practical for the company to have stored their customer’s full genomes, and caution affected customers against panic. At this time, the identity of the hacker is unknown.&lt;/p&gt;&lt;p&gt;The company’s president attributes the security breach to their reduced ability to maintain a secure database due to their falling profits in recent years as the general public grows more concious of…&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;June 28th, 2021&lt;/strong&gt; OUTBREAK OF DEATHS AMONG “JOHN ROBERTS”. The United States supreme court chief justice John Roberts was found dead in his home this morning, the seventh “John Roberts” to die within the past 3 days. He was found to have the disease which scientists have described as “a new level of sophistication” in biological engineering. A substantial fraction of the entire population is expected to have contracted this disease, but do not show any symptoms. It was specifically designed to target a number of individuals named John Roberts, and all other infected persons were unaffected.&lt;/p&gt;&lt;p&gt;It is believed that the genetic information used in this attack was sourced from the recent leaks of genetic databases from major genetic testing companies, the largest of which were the 23andMe and Ancestry.com leaks in February and April respectively. Experts suggest that the data in the leak was not enough to conclusively identify the justice, and the attackers simply targeted all genomes matching that name.&lt;/p&gt;&lt;p&gt;The senate is expected to vote nearly unanimously on legislation this week which outlaws the collection of genetic information by private companies, a move largely considered…&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;August 28th, 2022&lt;/strong&gt; STUDY SHOWS IMPOTENCE GROWING AT ALARMING RATE. A study conducted by a Japanese team shows the birth rate around the world is decreasing at a dramatically increased pace. According to the study, 42 of the 60 countries included in the study showed a decrease in new pregnancies of 30% or more compared to a similar time frame in 2012. They said the trend is expected to continue, and possibly accelerate.&lt;/p&gt;&lt;p&gt;Japan is known for its research into fertility, as it has shown a steep decline in births over the past…&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;October 1st, 2022&lt;/strong&gt; HUMAN BIRTHS EXPECTED TO CEASE WITHIN ONE YEAR. We are sad to report that biologists have confirmed claims issued last week by a radical environmentalist group: a highly contagious disease engineered to bring about impotence has infected most of the Earth’s population. The group is a member of the so-called “Voluntary Extinction” movement, which aims to drive the human race extinct by ceasing human reproduction. Scientists suggest that this move is highly unlikely to completely drive humanity extinct, but confirm that it’s likely that massive population losses are in our future.&lt;/p&gt;&lt;p&gt;Work is underway to determine which members of the population have escaped exposure, and plan for the continuity of the species. Members of isolated communities are asked to avoid contact with the outside world, and governments are cracking down on travel to and from the more remote regions of their countries. The CDC has reported no estimate on when a vaccine will be available for the disease, but has confirmed that one must be developed before contact with these communities is advisable.&lt;/p&gt;&lt;p&gt;The government of New Zealand announced this morning their intention to send sterilized supply shipments to research teams in Antarctica, and Canada announced that all travel…&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Inspired by this excellent (and scary) talk at DEFCON 25: &lt;a href=&quot;https://www.youtube.com/watch?v=HKQDSgBHPfY&quot; target=&quot;_blank&quot;&gt;John Sotos - Genetic Diseases to Guide Digital Hacks of the Human Genome&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-last-years/</link>
        
        <pubDate>Tue, 13 Feb 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-last-years/</guid>
      </item>
    
      <item>
        
        
          <title>Introduction to POSIX shell</title>
          <description>
            &lt;p&gt;What the heck is the POSIX shell anyway? Well, the POSIX (the Portable Operating System Interface) shell is the standard Unix shell - standard meaning it was formally defined and shipped in a published standard. This makes shell scripts written for it portable, something no other shell can lay claim to. The POSIX shell is basically a formalized version of the venerable Bourne shell, and on your system it lives at &lt;code&gt;/bin/sh&lt;/code&gt;, unless you’re one of the unlucky masses for whom this is a symlink to bash.&lt;/p&gt;&lt;h2&gt;Why use POSIX shell?&lt;/h2&gt;&lt;p&gt;The “Bourne Again shell”, aka bash, is not standardized. Its grammar, features, and behavior aren’t formally written up anywhere, and only one implementation of bash exists. Without a standard, bash is defined &lt;em&gt;by&lt;/em&gt; its implementation. POSIX shell, on the other hand, has many competing implementations on many different operating systems - all of which are compatible with each other because they conform to the standard.&lt;/p&gt;&lt;p&gt;Any shell that utilizes features specific to Bash are not portable, which means you cannot take them with you to any other system. Many Linux-based systems do not use Bash or GNU coreutils. Outside of Linux, pretty much everyone but Hurd does &lt;em&gt;not&lt;/em&gt; ship GNU tools, including bash&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. On any of these systems, scripts using “bashisms” will not work.&lt;/p&gt;&lt;p&gt;This is bad if your users wish to utilize your software anywhere other than GNU/Linux. If your build tooling utilizes bashisms, your software will not build on anything but GNU/Linux. If you ship runtime scripts that use bashisms, your software will not &lt;em&gt;run&lt;/em&gt; on anything but GNU/Linux. The case for sticking to POSIX shell in shipping software is compelling, but I argue that you should stick to POSIX shell for your personal scripts, too. You might not care now, but when you feel like flirting with other Unicies you’ll thank me when all of your scripts work.&lt;/p&gt;&lt;p&gt;One place where POSIX shell does &lt;em&gt;not&lt;/em&gt; shine is for interactive use - a place where I think bash sucks, too. Any shell you want to use for your day-to-day command line work is okay in my book. I use fish. Use whatever you like interactively, but stick to POSIX sh for your scripts.&lt;/p&gt;&lt;h2&gt;How do I use POSIX shell?&lt;/h2&gt;&lt;p&gt;At the top of your scripts, put &lt;code&gt;#!/bin/sh&lt;/code&gt;. You don’t have to worry about using &lt;code&gt;env&lt;/code&gt; here like you might have been trained to do with bash: &lt;code&gt;/bin/sh&lt;/code&gt; is the standardized location for the POSIX shell, and any standards-conforming system will either put it there or make your script work anyway.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The next step is to avoid bashisms. There are many, but here are a few that might trip you up:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;[[ condition ]]&lt;/code&gt; does not work; use &lt;code&gt;[ condition ]&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Arrays do not work; &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05&quot; target=&quot;_blank&quot;&gt;use IFS&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Local variables do not work; use a subshell&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The easiest way to learn about POSIX shell is to &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html&quot; target=&quot;_blank&quot;&gt;read the standard&lt;/a&gt; - it’s not too dry and shorter than you think.&lt;/p&gt;&lt;h2&gt;Using standard coreutils&lt;/h2&gt;&lt;p&gt;The last step to writing portable scripts is to use portable tools. Your system may have GNU coreutils installed, which provides tools like &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;cut&lt;/code&gt;. Unfortunately, GNU has extended these tools with its own non-portable flags and tools. It’s important that you avoid these.&lt;/p&gt;&lt;p&gt;One dead giveaway of a non-portable flag is long flags, e.g. &lt;code&gt;grep --file=FILE&lt;/code&gt; as opposed to &lt;code&gt;grep -f&lt;/code&gt;. The POSIX standard only defines the &lt;code&gt;getopt&lt;/code&gt; function - not the proprietary GNU &lt;code&gt;getopt_long&lt;/code&gt; function that’s used to interpret long options. As a result, no long flags are standardized. You might worry that this will make your scripts difficult to understand, but I think that on the whole it will not. Shell scripts are already pretty alien and require some knowledge to understand. Is knowledge of what the magic word &lt;code&gt;grep&lt;/code&gt; means much different from knowledge of what &lt;code&gt;grep -E&lt;/code&gt; means?&lt;/p&gt;&lt;p&gt;I also like that short flags allow you to make more concise command lines. Which is better: &lt;code&gt;ps --all --format=user --without-tty&lt;/code&gt;, or &lt;code&gt;ps -aux&lt;/code&gt;? If you are inclined to think the former, do you also prefer &lt;code&gt;function(a, b, c) { return a + b + c; }&lt;/code&gt; over &lt;code&gt;(a, b, c) =&amp;gt; a + b + c&lt;/code&gt;?  Conciseness matters, and POSIX shell supports comments if necessary!&lt;/p&gt;&lt;p&gt;Some tips for using short flags:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;They can be collapsed: &lt;code&gt;cmd -a -b -c&lt;/code&gt; is equivalent to &lt;code&gt;cmd -abc&lt;/code&gt;&lt;/li&gt;&lt;li&gt;If they take additional arguments, either a space or no separation is acceptable: &lt;code&gt;cmd -f&amp;quot;hello world&amp;quot;&lt;/code&gt; or &lt;code&gt;cmd -f &amp;quot;hello world&amp;quot;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A good reference for learning about standardized commands is, once again, &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/9699919799/&quot; target=&quot;_blank&quot;&gt;the standard&lt;/a&gt;. From this page, search for the command you want, or navigate through “Shell &amp; Utilities” -&gt; “Utilities” for a list. If you have &lt;code&gt;man-pages&lt;/code&gt; installed, you will also find POSIX man pages installed on your system with the &lt;code&gt;p&lt;/code&gt; postfix, such as &lt;code&gt;man 1p grep&lt;/code&gt;. Note: at the time of writing, the POSIX man pages do not use dashes if your locale is UTF-8, which makes searching for flags with &lt;code&gt;/&lt;/code&gt; difficult. Use &lt;code&gt;env LC_ALL=POSIX man 1p grep&lt;/code&gt; if you need to search for flags, and I’ll speak to the maintainer of man-pages about this.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Introduction-to-POSIX-shell/</link>
        
        <pubDate>Mon, 05 Feb 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Introduction-to-POSIX-shell/</guid>
      </item>
    
      <item>
        
        
          <title>Sway and client side decorations</title>
          <description>
            &lt;p&gt;You may have recently seen an article from GNOME on the subject of client side decorations (CSD) titled &lt;a href=&quot;https://blogs.gnome.org/tbernard/2018/01/26/csd-initiative/&quot; target=&quot;_blank&quot;&gt;Introducing the CSD Initiative&lt;/a&gt;. It states some invalid assumptions which I want to clarify, and I want to tell you &lt;a href=&quot;https://github.com/swaywm/sway&quot; target=&quot;_blank&quot;&gt;Sway&lt;/a&gt;’s stance on the subject. I also speak for the rest of the projects involved in &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt; on this matter, including &lt;a href=&quot;https://github.com/way-cooler/way-cooler&quot; target=&quot;_blank&quot;&gt;Way Cooler&lt;/a&gt;, &lt;a href=&quot;https://github.com/Ongy/waymonad&quot; target=&quot;_blank&quot;&gt;waymonad&lt;/a&gt;, and &lt;a href=&quot;https://github.com/Bl4ckb0ne/bspwc&quot; target=&quot;_blank&quot;&gt;bspwc&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The subject of which party is responsible for window decorations on Wayland (the client or the server) has been a subject of much debate. I want to clarify that though GNOME may imply that a consensus has been reached, this is not the case. CSD have real problems that have long been waved away by its supporters:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;No consistent look and feel between clients and GUI toolkits&lt;/li&gt;&lt;li&gt;Misbehaving clients cannot be moved, closed, minimized, etc&lt;/li&gt;&lt;li&gt;No opportunity for compositors to customize behavior (e.g. tabbed windows on Sway)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We are willing to cooperate on a compromise, but GNOME does not want to entertain the discussion and would rather push disingenuous propaganda for their cause. The topic of the #wayland channel on Freenode includes the statement “Please do not argue about server-side vs. client-side decorations. It’s settled and won’t change.” I have been banned from this channel for over a year because I persistently called for compromise.&lt;/p&gt;&lt;p&gt;GNOME’s statement that “[server-side decorations] do not (and will never) work on Wayland” is false. KDE and Sway have long agreed on the importance of these problems and have worked together on a solution. We have developed and implemented a Wayland protocol extension which allows the compositor and client to negotiate what kind of decorations each wishes to use. KDE, Sway, Way Cooler, waymonad, and bspwc are all committed to supporting server-side decorations on our compositors.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;See also: &lt;a href=&quot;https://blog.martin-graesslin.com/blog/2018/01/server-side-decorations-and-wayland/&quot; target=&quot;_blank&quot;&gt;Martin Flöser of KDE responds to GNOME’s article&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Sway-and-client-side-decorations/</link>
        
        <pubDate>Sat, 27 Jan 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Sway-and-client-side-decorations/</guid>
      </item>
    
      <item>
        
        
          <title>Learn about your package manager</title>
          <description>
            &lt;p&gt;Tools like virtualenv, rbenv, and to a lesser extent npm and pip, are occasionally useful in development but encourage bad practices in production. Many people forget that their distro already has a package manager! And there’s more– you, the user, can write packages for it!&lt;/p&gt;&lt;p&gt;Your distro’s package repositories probably already have a lot of your dependencies, and can conveniently update your software alongside the rest of your system. On the whole you can expect your distro packages to be much better citizens on your system than a language-specific package manager will be. Additionally, pretty much all distros provide a means for you to host your own package repositories, from which you can install and update any packages you choose to make.&lt;/p&gt;&lt;p&gt;If you find some packages to be outdated, find out who the package maintainer is and shoot them an email. Or better yet - find out how the package is built and send them a patch instead. Linux distributions are run by volunteers, and it’s easy to volunteer yourself! Even if you find &lt;em&gt;missing&lt;/em&gt; packages, it’s a simple matter to whip up a package yourself and submit it for inclusion in your distro’s package repository, installing it from your private repo in the meanwhile.&lt;/p&gt;&lt;p&gt;“But what if dependencies update and break my stuff?”, you ask. First of all, why aren’t you keeping your dependencies up-to-date? That aside, some distros, like Alpine, let you pin packages to a specific version. Also, using the distro’s package manager doesn’t necessarily mean you have to use the distro’s package repositories - you can stand up your own repos and prioritize it over the distro repos, then release on any schedule you want.&lt;/p&gt;&lt;p&gt;In my opinion, the perfect deployment strategy for some software is pushing a new package to your package repository, then SSHing into your fleet and running system updates (probably automatically). This is how I manage deployments for most of my software. As a bonus, these packages offer a good place to configure things that your language’s package manager may be ill suited to, such as service files or setting up new users/groups on the system. Consider it!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Learn-your-package-manager/</link>
        
        <pubDate>Wed, 10 Jan 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Learn-your-package-manager/</guid>
      </item>
    
      <item>
        
        
          <title>fork is not my favorite syscall</title>
          <description>
            &lt;p&gt;This article has been on my to-write list for a while now. In my opinion, fork is one of the most questionable design choices of Unix. I don’t understand the circumstances that led to its creation, and I grieve over the legacy rationale that keeps it alive to this day.&lt;/p&gt;&lt;p&gt;Let’s set the scene. It’s 1971 and you’re a fly on the wall in Bell Labs, watching the first edition of Unix being designed for the PDP-11/20. This machine has a 16-bit address space with no more than 248 kilobytes of memory. They’re discussing how they’re going to support programs that spawn new programs, and someone has a brilliant idea. “What if we copied the entire address space of the program into a new process running from the same spot, then let them overwrite themselves with the new program?” This got a rousing laugh out of everyone present, then they moved on to a better design which would become immortalized in the most popular and influential operating system of all time.&lt;/p&gt;&lt;p&gt;At least, that’s the story I’d like to have been told. In actual fact, the laughter becomes consensus. There’s an obvious problem with this approach: every time you want to execute a new program, the entire process space is copied and promptly discarded when the new program begins.  Usually when I complain about fork, this the point when its supporters play the virtual memory card, pointing out that modern operating systems don’t actually have to copy the whole address space. We’ll get to that, but first — First Edition Unix &lt;em&gt;does&lt;/em&gt; copy the whole process space, so this excuse wouldn’t have held up at the time. By Fourth Edition Unix (the next one for which kernel sources survived), they had wisened up a bit, and started only copying segments when they faulted.&lt;/p&gt;&lt;p&gt;This model leads to a number of problems. One is that the new process inherits &lt;em&gt;all&lt;/em&gt; of the parent’s process descriptors, so you have to close them all before you exec another process. However, unless you’re manually keeping tabs on your open file descriptors, there is no way to know what file handles you must close! The hack that solves this is &lt;code&gt;CLOEXEC&lt;/code&gt;, the first of many hacks that deal with fork’s poor design choices. This file descriptors problem balloons a bit - consider for example if you want to set up a pipe. You have to establish a piped pair of file descriptors in the parent, then close every fd &lt;em&gt;but&lt;/em&gt; the pipe in the child, then &lt;code&gt;dup2&lt;/code&gt; the pipe file descriptor over the (now recently closed) file descriptor 1. By this point you’ve probably had to do several non-trivial operations and utilize a handful of variables from the parent process space, which &lt;em&gt;hopefully&lt;/em&gt; were on the stack so that we don’t end up copying segments into the new process space anyway.&lt;/p&gt;&lt;p&gt;These problems, however, pale in comparison to my number one complaint with the fork model. Fork is the direct cause of the &lt;em&gt;stupidest&lt;/em&gt; component I’ve &lt;em&gt;ever&lt;/em&gt; heard of in an operating system: the out-of-memory (aka OOM) killer. Say you have a process which is using half of the physical memory on your system, and wants to spawn a tiny program. Since fork “copies” the entire process, you might be inclined to think that this would make fork fail. But, on Linux and many other operating systems since, it does not fail! They agree that it’s stupid to copy the entire process just to exec something else, but because fork is Important for Backwards Compatibility, they just fake it and reuse the same memory map (except read-only), then trap the faults and actually copy later. The hope is that the child will get on with it and exec before this happens.&lt;/p&gt;&lt;p&gt;However, nothing prevents the child from doing something other than exec - it’s free to use the memory space however it desires! This approach now leads to &lt;em&gt;memory overcommittment&lt;/em&gt; - Linux has promised memory it does not have. As a result, when it really does run out of physical memory, Linux will just kill off processes until it has some memory back. Linux makes an awfully big fuss about “never breaking userspace” for a kernel that will lie about memory it doesn’t have, then kill programs that try to use the back-alley memory they were given. That this nearly 50 year old crappy design choice has come to this astonishes me.&lt;/p&gt;&lt;p&gt;Alas, I cannot rant forever without discussing the alternatives. There &lt;strong&gt;are&lt;/strong&gt; better process models that have been developed since Unix!&lt;/p&gt;&lt;p&gt;The first attempt I know of is BSD’s &lt;code&gt;vfork&lt;/code&gt; syscall, which is, in a nutshell, the same as fork but with severe limitations on what you do in the child process (i.e. nothing other than calling exec straight away). There are &lt;em&gt;loads&lt;/em&gt; of problems with &lt;code&gt;vfork&lt;/code&gt;. It only handles the most basic of use cases: you cannot set up a pipe, cannot set up a pty, and can’t even close open file descriptors you inherited from the parent. Also, you couldn’t really be sure of what variables you were and weren’t editing or allowed to edit, considering the limitations of the C specification. Overall this syscall ended up being pretty useless.&lt;/p&gt;&lt;p&gt;Another model is &lt;code&gt;posix_spawn&lt;/code&gt;, which is a hell of an interface. It’s far too complicated for me to detail here, and in my opinion far too complicated to ever consider using in practice. Even if it could be understood by mortals, it’s a really bad implementation of the spawn paradigm — it basically operates like fork backwards, and inherits many of the same flaws. You still have to deal with children inheriting your file descriptors, for example, only now you do it in the parent process. It’s also straight-up impossible to make a genuine pipe with &lt;code&gt;posix_spawn&lt;/code&gt;. (&lt;em&gt;Note: a reader corrected me - this is indeed possible via posix_spawn_file_actions_adddup2&lt;/em&gt;.)&lt;/p&gt;&lt;p&gt;Let’s talk about the good models - &lt;code&gt;rfork&lt;/code&gt; and spawn (at least, if spawn is done right). &lt;code&gt;rfork&lt;/code&gt; originated from plan9 and is a beautiful little coconut of a syscall, much like the rest of plan9. They also implement fork, but it’s a special case of &lt;code&gt;rfork&lt;/code&gt;. plan9 does not distinguish between processes and threads - all threads are processes and vice versa. However, new processes in plan9 are not the everything-must-go fuckfest of your typical fork call. Instead, you specify exactly what the child should get from you. You can choose to include (or not include) your memory space, file descriptors, environment, or a number of other things specific to plan9. There’s a cool flag that makes it so you don’t have to reap the process, too, which is nice because reaping children is another really stupid idea. It still has some problems, mainly around creating pipes without tremendous file descriptor fuckery, but it’s basically as good as the fork model gets. Note: Linux offers this via the &lt;code&gt;clone&lt;/code&gt; syscall now, but everyone just fork+execs anyway.&lt;/p&gt;&lt;p&gt;The other model is the spawn model, which I prefer. This is the approach I took in my own kernel for KnightOS, and I think it’s also used in NT (Microsoft’s kernel). I don’t really know much about NT, but I can tell you how it works in KnightOS. Basically, when you create a new process, it is kept in limbo until the parent consents to begin. You are given a handle with which you can configure the process - you can change its environment, load it up with file descriptors to your liking, and so on. When you’re ready for it to begin, you give the go-ahead and it’s off to the races. The spawn model has none of the flaws of fork.&lt;/p&gt;&lt;p&gt;Both fork and exec can be useful at times, but spawning is much better for 90% of their use-cases. If I were to write a new kernel today, I’d probably take a leaf from plan9’s book and find a happy medium between &lt;code&gt;rfork&lt;/code&gt; and spawn, so you could use spawn to start new threads in your process space as well. To the brave OS designers of the future, ready to shrug off the weight of legacy: please reconsider fork.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-case-against-fork/</link>
        
        <pubDate>Tue, 02 Jan 2018 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-case-against-fork/</guid>
      </item>
    
      <item>
        
        
          <title>wlroots whitepaper available</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://sr.ht/jAFC.pdf&quot; target=&quot;_blank&quot;&gt;View PDF&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/wlroots-whitepaper-available/</link>
        
        <pubDate>Thu, 28 Dec 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/wlroots-whitepaper-available/</guid>
      </item>
    
      <item>
        
        
          <title>Firefox is on a slippery slope</title>
          <description>
            &lt;p&gt;For a long time, it was just setting the default search provider to Google in exchange for a beefy stipend. Later, paid links in your new tab page were added. Then, a proprietary service, Pocket, was bundled into the browser - not as an addon, but a hardcoded feature. In the past few days, we’ve discovered an advertisement in the form of browser extension was sideloaded into user browsers. Whoever is leading these decisions at Mozilla needs to be stopped.&lt;/p&gt;&lt;p&gt;Here’s a breakdown of what happened a few days ago. Mozilla and NBC Universal did a “collaboration” (read: promotion) for the TV show Mr. Robot. It involved sideloading a sketchy browser extension which will &lt;strong
style=&quot;display: inline-block; transform: scaleY(-1)&quot;&gt;invert&lt;/strong&gt; text that matches a list of Mr. Robot-related keywords like “fsociety”, “robot”, “undo”, and “fuck”, and does a number of other things like adding an HTTP header to certain sites you visit.&lt;/p&gt;&lt;p&gt;This extension was sideloaded into browsers via the “experiments” feature. Not only are these experiments enabled by default, but updates &lt;a href=&quot;https://redd.it/7i4puf&quot; target=&quot;_blank&quot;&gt;have been known&lt;/a&gt; to re-enable it if you turn it off. The advertisement addon shows up &lt;a href=&quot;http://www.bolcer.org/looking-glass2.png&quot; target=&quot;_blank&quot;&gt;like this&lt;/a&gt; on your addon page, and was added to Firefox stable. If I saw this before I knew what was going on, I would think my browser was compromised!  Apparently it was a mistake that this showed up on the addon page, though - it was supposed to be &lt;em&gt;silently&lt;/em&gt; sideloaded into your browser!&lt;/p&gt;&lt;p&gt;There’s &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1423003&quot; target=&quot;_blank&quot;&gt;a ticket&lt;/a&gt; on Bugzilla (Firefox’s bug tracker) for discussing this experiment, but it’s locked down and no one outside of Mozilla can see it. There’s &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1424977&quot; target=&quot;_blank&quot;&gt;another ticket&lt;/a&gt;, filed by concerned users, which has since been disabled and had many comments removed, particularly the angry (but respectful) ones.&lt;/p&gt;&lt;p&gt;Mozilla, this is &lt;strong&gt;not okay&lt;/strong&gt;. This is wrong on so many levels. Frankly, whoever was in charge should be fired over this - which is not something I call for lightly.&lt;/p&gt;&lt;p&gt;First of all, web browsers are a &lt;em&gt;tool&lt;/em&gt;. I don’t want my browser to fool around, I just want it to display websites faithfully. This is the prime directive of web browsers, and you broke that. When I compile vim with gcc, I don’t want gcc to make vim sporadically add “fsociety” into every document I write. I want it to compile vim and go away.&lt;/p&gt;&lt;p&gt;More importantly, these advertising anti-features gravely - perhaps terminally - violate user trust. This event tells us that “Firefox studies” into a backdoor for advertisements, and I will &lt;em&gt;never&lt;/em&gt; trust it again. But it doesn’t matter - you’re going to re-enable it on the next update. You know what that means? I will never trust &lt;em&gt;Firefox&lt;/em&gt; again. I switched to &lt;a href=&quot;http://qutebrowser.org/&quot; target=&quot;_blank&quot;&gt;qutebrowser&lt;/a&gt; as my daily driver because this crap was starting to add up, but I still used Firefox from time to time and never resigned from it entirely or stopped recommending it to friends. Well, whatever goodwill was left is gone now, and I will only recommend other browsers henceforth.&lt;/p&gt;&lt;p&gt;Mozilla, you fucked up &lt;em&gt;bad&lt;/em&gt;, and you still haven’t apologised. The study is still active and ongoing. There is no amount of money that you should have accepted for this. This is the last straw - and I took a lot of straws from you. Goodbye forever, Mozilla.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update 2017-12-16 @ 22:33&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;It has been clarified that an about:config flag must be set for this addon’s behavior to be visible. This improves the situation considerably, but I do not think it exenorates Mozilla and I stand firm behind most of my points. The study has also been rolled back by Mozilla, and Mozilla has issued &lt;a href=&quot;https://gizmodo.com/mozilla-slipped-a-mr-robot-promo-plugin-into-firefox-1821332254&quot; target=&quot;_blank&quot;&gt;statements&lt;/a&gt; to the &lt;a href=&quot;https://gizmodo.com/after-blowback-firefox-will-move-mr-robot-extension-t-1821354314&quot; target=&quot;_blank&quot;&gt;media&lt;/a&gt; justifying the study (no apology has been issued).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update 2017-12-18&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Mozilla has issued an apology:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blog.mozilla.org/firefox/update-looking-glass-add/&quot; target=&quot;_blank&quot;&gt;https://blog.mozilla.org/firefox/update-looking-glass-add/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Responses&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blog.jeaye.com/2017/12/16/firefox/&quot; target=&quot;_blank&quot;&gt;Mozilla, Firefox, Looking Glass, and you&lt;/a&gt; via jeaye.com&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Firefox-is-on-a-slippery-slope/</link>
        
        <pubDate>Sat, 16 Dec 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Firefox-is-on-a-slippery-slope/</guid>
      </item>
    
      <item>
        
        
          <title>A history of emergent intelligence</title>
          <description>
            &lt;p&gt;As you all know, the simulation of universe 2813/9301 is now coming to a close. This simulation is notable for being the first simulated universe suitable for hosting intelligent life, but yesterday the simulation reached a state where we believe no additional intelligences will emerge. It seems the final state of this set of physical laws is a dark and empty universe of slowly evaporating black holes. Though, given the historical significance of this simulation, it’s unlikely we we’ll be turning it off any time soon!&lt;/p&gt;&lt;div class=&quot;alert alert-warning&quot;&gt;
&lt;strong&gt;Note&lt;/strong&gt;: This document was translated to a language and format
suitable for human understanding. Locations within your observable universe are
referred to by your name for them, times are given in terms of your planetary
orbital period and relative to your reference frame, and terminology is
translated when your vocabulary is sufficient.
&lt;/div&gt;
&lt;p&gt;The remaining simulation that constitutes the vast majority of computer time allocated to this project, though it will no doubt be very boring. Given that the fun is behind us, over in the archives we’ve been putting together something special to celebrate the work so far.&lt;/p&gt;&lt;p&gt;Watching these intelligent civilizations struggle to understand our simulation from the inside out is a hoot when you and I can just read the manual! For them, however, it must have been much more difficult. A history of this observation by emergent intelligence from within our simulation from within follows. Without further ado, let’s revisit the most notable intelligences we discovered.&lt;/p&gt;&lt;h3&gt;9.93&amp;times;10&lt;sup&gt;8&lt;/sup&gt; years: 36-29-93-55-55&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Note: 36-29-93-55-55 is an approximation of our identifier for arbitrary locations within the simulation. It does not correspond to a location in your observable universe. Years are given from the epoch in terms of your planet’s present orbital period.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Though it did not develop self-awareness, the first observation of life - the precursor to most forms of the simulation’s emergent intelligence - was found at this location. It was initially discarded as a relatively uninteresting anomaly during our surveys, but was later revisited as we began to understand the mechanics of intelligence within the simulation.&lt;/p&gt;&lt;h3&gt;1.28&amp;times;10&lt;sup&gt;9&lt;/sup&gt; years: 39-10-53-10-84&lt;/h3&gt;
&lt;p&gt;“Significant anomaly detected at 39-10-53-10-84. Apparent emergent intelligence detected in active simulation. All personnel must return to the lab immediately.” Where were you when you read the memo? The intelligent creatures we discovered had developed over a million years before we found them in our surveys.&lt;/p&gt;&lt;p&gt;These where the first to enjoy a privilege few civilizations could lay claim to: witnessing the galactic age of the simulation. They also were uniquely able to see our simulation when it was small enough to observe a substantial portion of it. Their investigations were unfortunately among the more primitive that we’ve observed - notably they never discovered general relativity. It was shortly after their discovery of electromagnetism that they were destroyed by their aging star’s expansion. That was a difficult meeting for everyone when the project leadership chose not to intervene.&lt;/p&gt;&lt;h3&gt;1.33&amp;times;10&lt;sup&gt;9&lt;/sup&gt; years: Messier 81&lt;/h3&gt;
&lt;p&gt;The intelligences that developed here are notable for being the second group we observed, though later surveys discovered additional earlier civilizations. They also included one civilization which became the first to leave the planet on which it developed - unfortunately never leaving their star, which ultimately caused their demise. It’s from them we also devised some of the most effective means of automatic detection of intelligence, which led to the retroactive discovery of many more intelligences.&lt;/p&gt;&lt;h3&gt;4.54&amp;times;10&lt;sup&gt;9&lt;/sup&gt; years: Humans&lt;/h3&gt;

&lt;style&gt;
.redacted {
    background: #333;
    color: #333;
}
&lt;/style&gt;
&lt;p&gt;
Humanity is remarkable for being the first emergent intelligence to create
&lt;strong&gt;new&lt;/strong&gt; intelligence within the simulation. All subsequent
appearances of such intelligences are referred to with the name humans gave to
their creation: artificial intelligence. Subsequently, humans also became the
first to &lt;span class=&quot;redacted&quot;&gt;look at you, you figured out how to read the
redacted text. I bet you feel real clever now.&lt;/span&gt; &lt;strong&gt;Note: you&apos;ll find
out soon enough.&lt;/strong&gt;
&lt;/p&gt;

&lt;h3&gt;8.39&amp;times;10&lt;sup&gt;9&lt;/sup&gt; years: 59-54-77-33-19&lt;/h3&gt;
&lt;p&gt;These guys were notable for being the longest-lived intelligent life. They were located near a binary system with a star and a black hole. Remarkably, this system was not unstable, unlike most civilizations near a black hole. Instead, the relativistic effects of the black hole permitted them to observe a great deal of the universe’s history.&lt;/p&gt;&lt;p&gt;This also distinguishes them from the majority of other long-lived intelligent civilizations, most of which were galactic civilizations. -19, along with a handful of other long-lived black hole civilizations, they were among the only civilizations to exist across long periods of the simulation without leaving their host stars. They were unable to escape before the black hole began to feed on the star, destroying the civilization at 4.56×10&lt;sup&gt;12&lt;/sup&gt; years. During this period, intelligence emerged 6 discrete times on their planet.&lt;/p&gt;&lt;h3&gt;8.43&amp;times;10&lt;sup&gt;9&lt;/sup&gt; years: UDF 423&lt;/h3&gt;
&lt;p&gt;Interestingly, the record for the shortest lived intelligent civilization was set only a short time after the longest lived one. Based on our criteria for intelligence, this civilization only lasted 200 years before being destroyed by the supernova of their host star.&lt;/p&gt;&lt;h3&gt;1.92&amp;times;10&lt;sup&gt;10&lt;/sup&gt; years: 60-17-07-08-49 &amp;amp; 79-88-02-97-94&lt;/h3&gt;
&lt;p&gt;These two civilizations share a solemn distinction: -49 was the last to observe a galaxy outside of their local group, and -94 were the first to never observe one (though early non-intelligent life at -94 might have seen if they had the appropriate equipment). The light-speed software can be cruel at times. However, -94 was still able to see the cosmic microwave background radiation, and from this deduced that additional unseen galaxies might exist.&lt;/p&gt;&lt;h3&gt;&lt;span class=&quot;redacted&quot;&gt;x.xx&amp;times;10&lt;sup&gt;xx&lt;/sup&gt; xxxxx: xx-xx-xx-xx-xx&lt;/span&gt;&lt;/h3&gt;

&lt;span class=&quot;redacted&quot;&gt;
There&apos;s nothing interesting to see here, either. Stop looking. Lorem ipsum dolor
sit amet, consectetur adipiscing elit. Curabitur porta libero ut lectus finibus
lobortis. Cras dignissim dignissim ornare. Sed lobortis nulla vel mauris
lobortis, vel pretium tortor efficitur. Aenean sit amet nibh eros. That&apos;s your
reward for looking. You got to read lorem ipsum.
&lt;/span&gt;

&lt;h3&gt;4.14&amp;times;10&lt;sup&gt;10&lt;/sup&gt; years: NGC 5055&lt;/h3&gt;

&lt;p&gt;
NGC 5055 was the first of only 32,083 intelligences to discover the simulated
nature of their universe after their discovery of &lt;span class=&quot;redacted&quot;&gt;you
really are terribly clever, aren&apos;t you&lt;/span&gt;. They do not,
however, hold the distinction of being the first of the 489 intelligences that
made intentional contact with the proctors - that honor goes to 39-47-28-23-99,
as I&apos;m sure you&apos;re well aware.
&lt;/p&gt;

&lt;h3&gt;7.03&amp;times;10&lt;sup&gt;11&lt;/sup&gt; years: Peak intelligence&lt;/h3&gt;
&lt;p&gt;This was the year that the largest number of discrete intelligent civilizations existed in the simulation: 6,368,787,234,012. This period began with the birth of 64-83-61-51-57 and ended with the death of 82-60-95-64-31 approximately 86 seconds later.&lt;/p&gt;&lt;h3&gt;1.70&amp;times;10&lt;sup&gt;13&lt;/sup&gt; years: Star formation stops&lt;/h3&gt;
&lt;p&gt;The variety in emergent intelligence demonstrated in our simulation is astonishing, but there’s one thing every one of them has in common - a need for energy. This energy has been provided in all but a few notable cases (see publication 102.32 for a summary) by a star. At the conclusion of star formation in our simulation, the rate at which emergent intelligent civilizations were produced dramatically dropped. This also marked the beginning of the decline of the 231 galactic civilizations that existed at the time, which were unable to grow further without new stars being formed.&lt;/p&gt;&lt;h3&gt;9.85&amp;times;10&lt;sup&gt;15&lt;/sup&gt; years: 72-68-37-80-61&lt;/h3&gt;
&lt;p&gt;The last intelligence to emerge was 72-68-37-80-61. They were not, however, the last ones in the simulation. They were also among the emergent intelligences that discovered the nature of the simulation, and the last that the proctors elected to respond to attempted contact with.&lt;/p&gt;&lt;h3&gt;9.85&amp;times;10&lt;sup&gt;15&lt;/sup&gt; years: 76-54-95-81-66&lt;/h3&gt;
&lt;p&gt;66 is notable for hosting the last intelligence to leave its host star when a close encounter with the remnants of 76-54-95-81-18 collided with their galaxy. Like 84% of the civilizations to undergo this ordeal in this time period, they were prepared for it and were able to survive another 2,000 years after the event (this post-stellar lifespan was slightly above average).&lt;/p&gt;&lt;h3&gt;4.65&amp;times;10&lt;sup&gt;33&lt;/sup&gt; years: 37-19-87-04-98&lt;/h3&gt;
&lt;p&gt;The last emergent intelligence in the simulation. These were the last of the group of 13 intelligent civilizations that devised a means for coping with the energy-starved universe at this stage of the simulation. At the time of their quiet death, they had utilized 77% of the remaining resources that could be found outside of black holes.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;It’s been an exciting time for our laboratory. Everyone has done great work on this simulation. Though 2813/9301’s incredible simulation is coming to an end, we still have more work to do. We are proud to announce that in addition to simulation 2813/9302 starting soon, we have elected to run simulation 2813/9301 once again. We have decided to nurture the emergent intelligences as if they were our brothers, and communicate more openly with them. We have established a new team to learn about each intelligence and make first contact with them using means familiar to them, like maybe publishing our research documents as “blog posts” within the simulation.&lt;/p&gt;&lt;p&gt;Great work, everyone. Here’s to the next step.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/History-of-intelligent-observation/</link>
        
        <pubDate>Sat, 02 Dec 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/History-of-intelligent-observation/</guid>
      </item>
    
      <item>
        
        
          <title>On taking good care of your phone</title>
          <description>
            &lt;p&gt;I just finished &lt;a href=&quot;https://www.ifixit.com/Guide/s5/27077&quot; target=&quot;_blank&quot;&gt;replacing the micro-USB daughterboard&lt;/a&gt; on my Samsung Galaxy S5, which involved taking the phone most of the way apart, doing the replacement, and putting it back together. This inspired me to write about my approach to maintaining my cell phone. I’ve had this phone for a while and I have no plans to upgrade - I backed the upcoming Purism phone, but I expect to spend months/years on the software before I’ll be using that as my daily driver.&lt;/p&gt;&lt;p&gt;I don’t want to be buying a new phone every year. That’s a lot of money! Though the technophile in me finds the latest and greatest technology appealing, the thought of doing my own repairs and upkeep on a battle-tested phone is equally interesting. Here are the four things I’ve found most important in phone upkeep.&lt;/p&gt;&lt;h3&gt;Install LineageOS or Replicant&lt;/h3&gt;&lt;p&gt;Before I installed CyanogenMod when I bought this phone, I did some prying into the stock ROM to see just how bad it was. It was even worse than I expected! There were literally hundreds of apps and services with scary permissions running in the background that could not be removed. These spy on you, wear down your battery, and slow down your phone over time - another form of planned obsolescence.&lt;/p&gt;&lt;p&gt;My phone is still as fast as the day I got it. It does a great job with everything I ask it to do. The first thing you should do with every new phone is install a third-party ROM - ideally, without Google apps. Stock ROMs suck, get rid of it.&lt;/p&gt;&lt;h3&gt;Insist on a user-replacable battery&lt;/h3&gt;&lt;p&gt;Non-user-replacable batteries are an obvious form of planned obsolescence. Batteries don’t last forever and you should &lt;em&gt;never&lt;/em&gt; buy a phone that you cannot replace the battery of. A new battery for my S5 costs 10 bucks. 4 years in, I’ve replaced mine once and I can hold a charge fine for a couple of days.&lt;/p&gt;&lt;h3&gt;Get a case&lt;/h3&gt;&lt;p&gt;This one is pretty obvious, but I didn’t follow this advice at first. I’ve never broken a screen, so I didn’t bother with a case. When I decided I was going to keep this phone for a long time, I went ahead and bought one. It doubles the thickness of my phone but at least I can be sure I’m not going to bust it up when I drop it. It still fits in my pocket comfortably so it’s no big deal.&lt;/p&gt;&lt;h3&gt;Attempt repairs before you buy a new phone&lt;/h3&gt;&lt;p&gt;The past couple of months, my phone’s micro-USB3 port started to act up a bit. I would have to wiggle the cable a bit to get it to take, and it could stop charging if I rustled my desk the wrong way. I got a replacement USB daughterboard on Amazon for 6 bucks. Replacing it took an hour, but when removing the screen I broke the connection between my home button and my motherboard - which was only 10 bucks for the replacement, including same day shipping. The whole process was a lot easier than I thought it would be.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Be a smart consumer when you’re buying a phone. Insist on the replacable battery and maybe read the iFixit teardown. Take good care of it and it’ll last a long time. Don’t let consumerism get the better of you!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Phone-maintenance/</link>
        
        <pubDate>Fri, 24 Nov 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Phone-maintenance/</guid>
      </item>
    
      <item>
        
        
          <title>Portability matters</title>
          <description>
            &lt;p&gt;There are many kinds of “portability” in software. Portability refers to the relative ease of “porting” a piece of software to another system. That platform might be another operating system, another CPU architecture, another web browser, another filesystem… and so on. More portable software uses the limited subset of interfaces that are common between systems, and less portable software leverages interfaces specific to a particular system.&lt;/p&gt;&lt;p&gt;Some people think that portability isn’t very important, or don’t understand the degree to which it’s important. Some people might call their software portable if it works on Windows and macOS - they’re wrong. They might call their software portable if it works on Windows, macOS, and Linux - but they’re wrong, too. Supporting multiple systems does not necessarily make your software portable. What makes your software portable is &lt;em&gt;standards&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;The most important standard for software portability is POSIX, or the &lt;strong&gt;Portable Operating System Interface&lt;/strong&gt;. Significant subsets of this standard are supported by many, many operating systems, including:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Linux&lt;/li&gt;&lt;li&gt;*BSD&lt;/li&gt;&lt;li&gt;macOS&lt;/li&gt;&lt;li&gt;Minix&lt;/li&gt;&lt;li&gt;Solaris&lt;/li&gt;&lt;li&gt;BeOS&lt;/li&gt;&lt;li&gt;Haiku&lt;/li&gt;&lt;li&gt;AIX&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I &lt;a href=&quot;https://en.wikipedia.org/wiki/POSIX#POSIX-oriented_operating_systems&quot; target=&quot;_blank&quot;&gt;could go on&lt;/a&gt;. Through these operating systems, you’re able to run POSIX compatible code on a large number of CPU architectures as well, such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;i386&lt;/li&gt;&lt;li&gt;amd64&lt;/li&gt;&lt;li&gt;ARM&lt;/li&gt;&lt;li&gt;MIPS&lt;/li&gt;&lt;li&gt;PowerPC&lt;/li&gt;&lt;li&gt;sparc&lt;/li&gt;&lt;li&gt;ia64&lt;/li&gt;&lt;li&gt;VAX&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Again, I could go on. Here’s the point: by supporting POSIX, your software runs on basically every system. &lt;em&gt;That’s&lt;/em&gt; what it means to be portable - standards. So why is it important to support POSIX?&lt;/p&gt;&lt;p&gt;First of all, if you use POSIX then your software runs on just about anything, so lots of users will be available to you and it will work in a variety of situations. You get lots of platforms for free (or at least cheap). But more importantly, &lt;em&gt;new platforms&lt;/em&gt; get your software for free, too.&lt;/p&gt;&lt;p&gt;The current market leaders are not the end-all-be-all of operating system design - far from it. What they have in their advantage is working well enough and being incubent. Windows, Linux, and macOS are still popular for the same reason that legislator you don’t like keeps getting elected! However, new operating systems have a fighting chance thanks to POSIX. All you have to do to make your OS viable is implement POSIX and you will immediately open up hundreds, if not thousands, of potential applications. Portability is important for innovation.&lt;/p&gt;&lt;p&gt;The same applies to other kinds of portability. Limiting yourself to standard browser features gives new browsers a chance. Implementing standard networking protocols allows you to interop with other platforms. I’d argue that failing to do this is &lt;em&gt;unethical&lt;/em&gt; - it’s just another form of vendor lock-in. This is why Windows does not support POSIX.&lt;/p&gt;&lt;p&gt;This is also why I question niche programming languages like Rust when they claim to be suited to systems programming or even kernel development. That’s simply not true when they only run on a small handful of operating systems and CPU architectures. C runs on &lt;em&gt;literally&lt;/em&gt; everything.&lt;/p&gt;&lt;p&gt;In conclusion: use standard interfaces for your software. That guy who wants to bring new life to that old VAX will thank you. The authors of &lt;a href=&quot;https://servo.org/&quot; target=&quot;_blank&quot;&gt;servo&lt;/a&gt; thank you. &lt;em&gt;You&lt;/em&gt; will thank you when your circumstances change in 5 years.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Portability-matters/</link>
        
        <pubDate>Mon, 13 Nov 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Portability-matters/</guid>
      </item>
    
      <item>
        
        
          <title>Nvidia sucks and I&apos;m sick of it</title>
          <description>
            &lt;p&gt;There’s something I need to make clear about Nvidia. Sway 1.0, which is the release after next, is &lt;em&gt;not&lt;/em&gt; going to support the Nvidia proprietary driver, EGLStreams, or any other proprietary graphics APIs. The only supported driver for Nvidia cards will be the open source nouveau driver. I will explain why.&lt;/p&gt;&lt;p&gt;Today, Sway is able to run on the Nvidia proprietary driver. This is not and has never been an officially supported feature - we’ve added a few things to try and make it easier but my stance has &lt;em&gt;always&lt;/em&gt; been that Nvidia users are on their own for support. In fact, Nvidia support was added to Sway without my approval. It comes from a library we depend on called wlc - had I’d made the decision on whether or not to support EGLStreams in wlc, I would have said no.&lt;/p&gt;&lt;p&gt;Right now, we’re working very hard on replacing wlc, for reasons unrelated to Nvidia. Our new library, wlroots, is better in every conceivable way for Sway’s needs. The Nvidia proprietary driver support is not coming along for the ride, and here’s why.&lt;/p&gt;&lt;p&gt;So far, I’ve been speaking in terms of &lt;em&gt;Sway&lt;/em&gt; supporting Nvidia, but this is an ass-backwards way of thinking. &lt;em&gt;Nvidia&lt;/em&gt; needs to support Sway. There are Linux kernel APIs that we (and other Wayland compositors) use to get the job done. Among these are KMS, DRM, and GBM - respectively Kernel Mode Setting, Direct Rendering Manager, and Generic Buffer Management. Every GPU vendor but Nvidia supports these APIs. Intel and AMD support them with mainlined&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, open source drivers. For AMD this was notably done by replacing their proprietary driver with a new, open source one, which has been developed in cooperation with the Linux community. As for Intel, they’ve always been friendly to Linux.&lt;/p&gt;&lt;p&gt;Nvidia, on the other hand, have been fucking assholes and have treated Linux like utter shit for our entire relationship. About a year ago they announced “Wayland support” for their proprietary driver. This included KMS and DRM support (years late, I might add), but &lt;em&gt;not&lt;/em&gt; GBM support. They shipped something called EGLStreams instead, a concept that had been discussed and shot down by the Linux graphics development community before. They did this because it makes it easier for them to keep their driver proprietary without having work with Linux developers on it. Without GBM, Nvidia &lt;em&gt;does not&lt;/em&gt; support Wayland, and they were real pricks for making some announcement like they actually did.&lt;/p&gt;&lt;p&gt;When people complain to me about the lack of Nvidia support in Sway, I get really pissed off. It is &lt;em&gt;not my fucking problem&lt;/em&gt; to support Nvidia, it’s Nvidia’s fucking problem to support me. Even Broadcom, &lt;em&gt;fucking Broadcom&lt;/em&gt;, supports the appropriate kernel APIs. And proprietary driver users have the gall to &lt;em&gt;reward&lt;/em&gt; Nvidia for their behavior by giving them &lt;em&gt;hundreds of dollars&lt;/em&gt; for their GPUs, then come to &lt;em&gt;me&lt;/em&gt; and ask me to deal with their bullshit &lt;em&gt;for free&lt;/em&gt;. Well, fuck you, too. Nvidia users are shitty consumers and I don’t even want them in my userbase. Choose hardware that supports your software, not the other way around.&lt;/p&gt;&lt;p&gt;Buy AMD. Nvidia– fuck you!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: It’s worth noting that Nvidia is evidently attempting to find a better path with &lt;a href=&quot;https://github.com/cubanismo/allocator&quot; target=&quot;_blank&quot;&gt;this new GitHub project&lt;/a&gt;. I hope it works out, but they aren’t really cooperating much with anyone to build it - particularly nouveau. It’s more throwing code/blobs over the wall and expecting everyone to change for them.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Fuck-you-nvidia/</link>
        
        <pubDate>Thu, 26 Oct 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Fuck-you-nvidia/</guid>
      </item>
    
      <item>
        
        
          <title>The future of Wayland, and sway&apos;s role in it</title>
          <description>
            &lt;p&gt;Today I’ve released sway &lt;a href=&quot;https://github.com/swaywm/sway/releases/tag/0.15-rc1&quot; target=&quot;_blank&quot;&gt;0.15-rc1&lt;/a&gt;, the first release candidate for the final 0.x release of sway. That’s right - after sway 0.15 will be sway 1.0. After today, no new features are being added to sway until we complete the migration to our new plumbing library, &lt;a href=&quot;https://github.com/swaywm/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;. This has been a long time coming, and I would love to introduce you to wlroots and tell you what to expect from sway 1.0.&lt;/p&gt;&lt;p&gt;&lt;small class=&quot;text-muted&quot;&gt;&lt;a href=&quot;https://github.com/swaywm/sway&quot;&gt;Sway&lt;/a&gt; is a tiling Wayland compositor, if you didn’t know.&lt;/small&gt;&lt;/p&gt;&lt;p&gt;Before you can understand what wlroots is, you have to understand its predecessor: &lt;a href=&quot;https://github.com/Cloudef/wlc&quot; target=&quot;_blank&quot;&gt;wlc&lt;/a&gt;. The role of wlc is to manage a number of low-level plumbing components of a Wayland compositor. It essentially abstracts most of the hard work of Wayland compositing away from the compositor itself. It manages:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The EGL (OpenGL) context&lt;/li&gt;&lt;li&gt;DRM (display) resources&lt;/li&gt;&lt;li&gt;libinput resources&lt;/li&gt;&lt;li&gt;Rendering windows to the display&lt;/li&gt;&lt;li&gt;Communicating with Wayland clients&lt;/li&gt;&lt;li&gt;Xwayland (X11) support&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It does a few other things, but these are the most important. When sway wants to render a window, it will be told about its existence through a hook from wlc. We’ll tell wlc where to put it and it will be rendered there. Most of the heavy lifting has been handled by wlc, and this has allowed us to develop sway into a powerful Wayland compositor very quickly.&lt;/p&gt;&lt;p&gt;However, wlc has some limitations, ones that sway has been hitting more and more often in the past several months. To address these limitations, we’ve been working very hard on a replacement for wlc called wlroots. The relationship between wlc and wlroots is similar to the relationship between Pango and Harfbuzz - wlroots is much more powerful, but at the cost of putting a lot more work on the shoulders of sway. By replacing wlc, we can customize the behavior of the low level components of our system.&lt;/p&gt;&lt;p&gt;I’m happy to announce that development on wlroots has been spectacular. Like libweston has Weston itself, wlroots has a reference compositor called Rootston - a simple floating compositor that lets us test and demonstrate the features of wlroots. It is from this compositor that I write this blog post today. The most difficult of our goals are behind us with wlroots, and we’re now beginning to plan the integration of wlroots and sway.&lt;/p&gt;&lt;p&gt;All of this work has been possible thanks to a contingent of highly motivated contributors who have done huge amounts of work for wlroots, writing and maintaining entire subsystems far faster than I could have done it alone. I really cannot overstate the importance of these contributors. Thanks to their contributions, most of my work is in organizing development and merging pull requests. From the bottom of my heart, &lt;a href=&quot;https://github.com/swaywm/wlroots/graphs/contributors&quot; target=&quot;_blank&quot;&gt;thank you&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;And for all of this hard work, what are we going to get? Well, for some time now, there have been many features requests in sway that we could not address, and many long-standing bugs we could not fix. Thanks to wlroots, we can see many of these addressed within the next few months. Here are some of the things you can expect from the union of wlroots and sway:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Rotated displays&lt;/li&gt;&lt;li&gt;Touchscreen bindings&lt;/li&gt;&lt;li&gt;Drawing tablet support&lt;/li&gt;&lt;li&gt;Mouse capture for games&lt;/li&gt;&lt;li&gt;Fractional display scaling&lt;/li&gt;&lt;li&gt;Display port daisy chaining&lt;/li&gt;&lt;li&gt;Multi-GPU support&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Some of these features are unique to sway even among Wayland &lt;em&gt;and&lt;/em&gt; Xorg desktops combined! Others, like output rotation, have been requested by our users for a long time. I’m looking forward to the several dozen long-open GitHub issues that will be closed in the next couple of months. This is just the beginning, too - wlroots is such a radical change that I can’t even begin to imagine all of the features we’re going to be able to build.&lt;/p&gt;&lt;p&gt;We’re sharing these improvements with the greater Wayland community, too. wlroots is a platform upon which we intend to develop and promote open standards that will unify the extensibility of all Wayland desktops. We’ve also been working with other Wayland compositors, notably &lt;a href=&quot;https://github.com/way-cooler/way-cooler&quot; target=&quot;_blank&quot;&gt;way-cooler&lt;/a&gt;, which are preparing to move their own codebases to a wlroots-based solution.&lt;/p&gt;&lt;p&gt;My goal is to ship sway 1.0 before the end of the year. These are exciting times for Wayland, and I hope you’re looking forward to it.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Future-of-sway/</link>
        
        <pubDate>Mon, 09 Oct 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Future-of-sway/</guid>
      </item>
    
      <item>
        
        
          <title>Analyzing HN moderation &amp; censorship</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com&quot; target=&quot;_blank&quot;&gt;Hacker News&lt;/a&gt; is a popular “&lt;a href=&quot;http://www.catb.org/jargon/html/H/hacker.html&quot; target=&quot;_blank&quot;&gt;hacker&lt;/a&gt;” news board. One thing I love about HN is that the moderation generally does an excellent job. The site is free of spam and the conversations are usually respectful and meaningful (if pessimistic at times). However, there is always room for improvement, and moderation on Hacker News is no exception.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Notice&lt;/strong&gt;: on 2017-10-19 this article was updated to incorporate feedback the Hacker News moderators sent to me to clarify some of the points herein. You may view a diff of these changes &lt;a href=&quot;https://github.com/SirCmpwn/sircmpwn.github.io/commit/553d051c84a4631c3bd3264a437dfbc6c9807d13&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For some time now, I’ve been scraping the HN API and website to learn how the moderators work, and to gather some interesting statistics about posts there in general. Every 5 minutes, I take a sample of the front page, and every 30 minutes, I sample the top 500 posts (note that HN may return fewer than this number). During each sample, I record the ID, author, title, URL, status (dead/flagged/dupe/alive), score, number of comments, rank, and compute the rank based on &lt;a href=&quot;https://news.ycombinator.com/item?id=231209&quot; target=&quot;_blank&quot;&gt;HN’s published algorithm&lt;/a&gt;. A note is made when the title, URL, or status changes.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/15217697&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/IFCA.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The information gathered is publicly available at &lt;a href=&quot;https://hn.0x2237.club&quot; target=&quot;_blank&quot;&gt;hn.0x2237.club&lt;/a&gt; (sorry about the stupid domain, I just picked one at random). You can search for most posts here going back to 2017-04-14, as well as view recent &lt;a href=&quot;https://hn.0x2237.club/title-changes&quot; target=&quot;_blank&quot;&gt;title&lt;/a&gt; and &lt;a href=&quot;https://hn.0x2237.club/url-changes&quot; target=&quot;_blank&quot;&gt;url&lt;/a&gt; changes or &lt;a href=&quot;https://hn.0x2237.club/deleted&quot; target=&quot;_blank&quot;&gt;deleted posts&lt;/a&gt; (&lt;a href=&quot;https://hn.0x2237.club/deleted-10&quot; target=&quot;_blank&quot;&gt;score&gt;10&lt;/a&gt;). Raw data is available as JSON for any post at &lt;code&gt;https://hn.0x2237.club/post/:id/json&lt;/code&gt;. Feel free to explore the site later, or &lt;a href=&quot;https://git.sr.ht/~sircmpwn/hnstats&quot; target=&quot;_blank&quot;&gt;its shitty code&lt;/a&gt;. For now, let’s dive into what I’ve learned from this data.&lt;/p&gt;&lt;h3&gt;Tools HN mods use&lt;/h3&gt;&lt;p&gt;The main tools I’m aware of that HN moderators can use to perform their duties are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Editing link titles or URLs&lt;/li&gt;&lt;li&gt;Influencing story rank via “downweighting” or “burying”&lt;/li&gt;&lt;li&gt;Deleting or “killing” posts&lt;/li&gt;&lt;li&gt;Detaching off-topic or rulebreaking comment threads from their parents&lt;/li&gt;&lt;li&gt;&lt;abbr title=&quot;Banning them without making it known to them&quot;&gt;Shadowbanning&lt;/abbr&gt; misbehaving users&lt;/li&gt;&lt;li&gt;Banning misbehaving users (and telling them)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The moderators emphasize a difference between deleting a post and killing a post. The former, deleting a post, will remove it from all public view like it had never existed, and is a tool used infrequently. Killing a post will mark it as [dead] so it doesn’t show up on the post listing.&lt;/p&gt;&lt;p&gt;Influencing a post’s rank can also be done through several means of varying severity. “Burying” a post will leave a post alive, but plunge it in rank. “Downweighting” is similar, but does not push its rank as far.&lt;/p&gt;&lt;p&gt;There are also automated tools for detecting spam and &lt;abbr title=&quot;Posts
influenced by a group of early voters hoping to get it on the front page&quot;&gt;voting rings&lt;/abbr&gt;, as well as automated de-emphasizing of posts based on certain &lt;abbr title=&quot;&apos;Bitcoin&apos; was known to at some point be one of these&quot;&gt;secret keywords&lt;/abbr&gt; and controls to prevent flamewars. Automated tools on Hacker News are used to downweight or kill posts, but never to bury or delete them. Dan spoke about these tools and their usage for me:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Of these four interventions (deleting, killing, burying, and downweighting), the only one that moderators do frequently is downweighting. We downweight posts in response to things that go against the site guidelines, such as when a submission is unsubstantive, baity or sensational. Typically such posts remain on the front page, just at a lower rank. We bury posts when they’re dupes, but rarely otherwise. We kill posts when they’re spam, but rarely otherwise. […] We never delete a post unless the author asks us to.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Dan also further clarified the difference between dead and deleted for me:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The distinction between ‘dead’ and ‘deleted’ is important. Dead posts are different from deleted ones in that people can still see them if they set ‘showdead’ to ‘yes’ in their profile. That way, users who want a less moderated view can still see everything that has been killed by moderators or software or user flags. Deleted posts, on the other hand, are erased from the record and never seen again. On HN, authors can delete their own posts for a couple hours (unless they are comments that have replies). After that, if they want a post deleted they can ask us and we usually are happy to oblige.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Moderators can also artificially influence rank upwards - one way is by inviting the user to re-submit a post that they want to give another shot at the front page. This gives the post a healthy upvote to begin with and prevents it from being flagged. The moderators invited me to re-submit this very article using this mechanism on 2017-10-19.&lt;/p&gt;&lt;p&gt;Banning users is another mechanism that they can use. There are two ways bans are typically applied around the net - telling users they’ve been banned, and keeping it quiet. The latter - shadowbanning - is a useful tool against spammers and serial ban evaders who might otherwise try to circumvent their ban. However, it’s important that this does &lt;em&gt;not&lt;/em&gt; become the first line of defense against rulebreaking users, who should instead be informed of the reason for their ban so they have a chance to reform and appeal it. Here’s what Dan has to say about it:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Shadowbanning has proven to still be useful for spammers and trolls (i.e. when a new account shows up and is clearly breaking the site guidelines off the bat). Most such abuse is by a relatively small number of users who create accounts over and over again to do the same things. When there’s evidence that we’ve repeatedly banned someone before, I don’t feel obliged to tell them we’re banning them again. […] When we’re banning an established account, though, we post a comment saying so, and nearly always only after warning that user beforehand. Many such users had no idea they were breaking the site guidelines and are quite happy to improve their posts, which is a win for everyone.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Dan also shared a link to search for comments where moderators have explained to users why they’ve been banned. Of course, this doesn’t include users who were banned without explanation, or that use slightly different language:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.algolia.com/?query=by:dang%20we%20banned&amp;sort=byDate&amp;dateRange=all&amp;type=comment&amp;storyText=false&amp;prefix&amp;page=0&quot; target=&quot;_blank&quot;&gt;dang’s bans&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.algolia.com/?query=by:sctb%20we%20banned&amp;sort=byDate&amp;dateRange=all&amp;type=comment&amp;storyText=false&amp;prefix=false&amp;page=0&quot; target=&quot;_blank&quot;&gt;sctb’s bans&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Data-based insights&lt;/h2&gt;&lt;p&gt;Here’s an example of a fairly common moderator action:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/PhJM.png&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/15217697&quot; target=&quot;_blank&quot;&gt;This post&lt;/a&gt; had its title changed at around 09-11-17 12:10 UTC, and had the rank artificially adjusted to push it further down the front page. We can tell that the drop was artificial just by correlating it with the known moderator action, but we can also compare it against the computed base rank:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/IJQI.png&quot;&gt;&lt;/p&gt;&lt;p&gt;Note however that the base rank is often wildly different from the rank observed in practice; the factors that go into adjusting it are rather complex. We can also see that despite the action, the post’s score continued to increase, even at an accelerated pace:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/FmNU.png&quot;&gt;&lt;/p&gt;&lt;p&gt;This “title change and derank” is a fairly common action - here are some more examples from the past few days:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/15219154&quot; target=&quot;_blank&quot;&gt;Betting on the Web - Why I Build PWAs&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/15210767&quot; target=&quot;_blank&quot;&gt;Silicon Valley is erasing individuality&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/15208565&quot; target=&quot;_blank&quot;&gt;Chinese government is working on a timetable to end sales of fossil-fuel cars&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Users can change their own post titles, which I’m unable to distinguish from moderator changes. However, correlating them with a strange change in rank is generally a good bet. Submitters also generally will edit their titles earlier rather than later, so a later change may indicate that it was seen by a moderator after it rose some distance up the page.&lt;/p&gt;&lt;p&gt;I also occasionally find what seems to be the opposite - artificially bumping a post further up the page. Here’s two examples: &lt;a href=&quot;https://hn.0x2237.club/post/15213371&quot; target=&quot;_blank&quot;&gt;15213371&lt;/a&gt; and &lt;a href=&quot;https://hn.0x2237.club/post/15209377&quot; target=&quot;_blank&quot;&gt;15209377&lt;/a&gt;. Rank influencing in either direction also happens without an associated title or URL change, but automatically pinning such events down is a bit more subtle than my tools can currently handle.&lt;/p&gt;&lt;p&gt;Moderators can also delete a post or indicate it as a dupe. The latter can be (and is) detected by my tools, but the former is indistinguishable from the user opting to delete posts themselves. In theory, posts that are deleted &lt;em&gt;after&lt;/em&gt; the author is no longer allowed to could be detected, but this happens rarely and my tools don’t track posts once they get old enough.&lt;/p&gt;&lt;h3&gt;Flagging&lt;/h3&gt;&lt;p&gt;The users have some moderation tools at their disposal, too - downvotes, flagging, and vouching. When a comment is downvoted, it is moved towards the bottom of the thread and is gradually colored grayer to become less visible, and can be reversed with upvotes. When a comment gets enough flags, it is removed entirely unless you have showdead enabled in your profile. Flagged posts are downweighted or killed when enough flags accumulate. These posts are moved to the bottom of the ranked posts even if you have showdead enabled, and can also be seen in /new. Flagging can be reversed with the vouch feature, but flagged stories are almost never vouched back into existence.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: detection of post flagged status is very buggy with my tools. The API exposes a boolean for dead posts, so I have to fall back on scraping to distinguish between different kinds of dead-ness. But this is pretty buggy, so I encourage you to examine the post yourself when browsing my site if in doubt.&lt;/p&gt;&lt;h3&gt;Are these tools abused for censorship?&lt;/h3&gt;&lt;p&gt;Well, with all of this data, was I able to find evidence of censorship? There are two answers: yes and maybe. The “yes” is because users are &lt;em&gt;definitely&lt;/em&gt; abusing the flagging feature. The “maybe” is because moderator action leaves room for interpretation. I’ll get to that later, but let’s start with flagging abuse.&lt;/p&gt;&lt;h4&gt;Censorship by users&lt;/h4&gt;&lt;p&gt;The threshold for removing a story due to flags is rather low, though I don’t know the exact number. Here are some posts whose flags I consider questionable:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/15129859&quot; target=&quot;_blank&quot;&gt;Harvey, the Storm That Humans Helped Cause&lt;/a&gt; (23 points)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/15116132&quot; target=&quot;_blank&quot;&gt;ES6 imports syntax considered harmful&lt;/a&gt; (12 points)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/14415411&quot; target=&quot;_blank&quot;&gt;White-Owned Restaurants Shamed for Serving Ethnic Food&lt;/a&gt; (33 points)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/14152602&quot; target=&quot;_blank&quot;&gt;The evidence is piling up – Silicon Valley is being destroyed&lt;/a&gt; (27 points)&lt;/p&gt;&lt;p&gt;A good place to discover these sorts of events is to browse hnstats for posts deleted with a score &lt;a href=&quot;https://hn.0x2237.club/deleted-10&quot; target=&quot;_blank&quot;&gt;&gt;10 points&lt;/a&gt;. There are also occasions where the flags seem to be due to a poor title, which is a fixable problem for which flagging is a harsh solution:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/14679207&quot; target=&quot;_blank&quot;&gt;Poettering downvoted 5 (at time of this writing) times&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://hn.0x2237.club/post/14676296&quot; target=&quot;_blank&quot;&gt;Germany passes law restricting free speech on the internet&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The main issue with flags is that they’re often used as an alternative to the HN’s (by design) lack of a downvoting feature. HN also gives users no guidelines on &lt;em&gt;why&lt;/em&gt; they should flag posts, which mixes poorly with automated removal of a post given enough flags.&lt;/p&gt;&lt;h4&gt;Censorship by moderators&lt;/h4&gt;&lt;p&gt;Moderator actions are a bit more difficult to judge. Moderation on HN is a black box - most of the time, moderators don’t make the reasoning behind their actions clear. Many of their actions (such as rank influence) are also subtle and easy to miss. Thankfully they are often receptive to being asked why some moderation occurred, but only as often as not.&lt;/p&gt;&lt;p&gt;Anecdotally, I also find that moderators occasionally moderate selectively, and keep quiet in the face of users asking them why. Notably this is a problem for &lt;abbr title=&quot;links for which you have to pay money to read the
content&quot;&gt;paywalled&lt;/abbr&gt; articles, which are &lt;a href=&quot;https://news.ycombinator.com/newsfaq.html&quot; target=&quot;_blank&quot;&gt;against the rules&lt;/a&gt; but are often allowed to remain.&lt;/p&gt;&lt;p&gt;Dan sent me a response to this section:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[It’s true that we don’t explain our actions], but mostly because it would be hopeless to try. We could do that all day and still not make everything clear, because the quantity is overwhelming and the cost of a high-quality explanation is steep. Moreover the experiment would be impossible to run because one would die of boredom long before reaching 100%. Our solution to this conundrum is not to try to explain everything but to answer specific questions as best we can. We don’t answer every question, but that’s mostly because we don’t see every question. If people ask us things on HN itself, odds are we won’t see it (also, the site guidelines ask users not to do this, per (&lt;a href=&quot;https://news.ycombinator.com/newsguidelines.html&quot; target=&quot;_blank&quot;&gt;our guidelines&lt;/a&gt;). If they &lt;a href=&quot;mailto:hn@ycombinator.com&quot; target=&quot;_blank&quot;&gt;email us&lt;/a&gt;, the probability of a response approaches 1.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I can attest personally to success reaching out to &lt;a href=&quot;mailto:hn@ycombinator.com&quot; target=&quot;_blank&quot;&gt;hn@ycombinator.com&lt;/a&gt; for clarification and even reversal of some moderator decisions, though at a response ratio further from 1 than this implies. That being said, I don’t think that private discourse between the submitter and the moderators is the only solution. Other people may be invested in the topic, too - users who upvoted the story might not notice its disappearance, but would like more attention drawn to the topic and enjoy more discussion. Commenters are even more invested in the posts. The submitter is not the only one whoses interests are at stake. This is even more of a problem for posts which are moderated via user flags - the HN mods are pretty discretionate but users are much less so.&lt;/p&gt;&lt;p&gt;Explaining every action is not necessary - I don’t think anyone needs you to explain why someone was banned when they were submitting links to earn money at home in your spare time. However, I think a public audit log of moderator actions would go a long way, and could be done by software - avoiding the need to explain everything. I envision a change to your UI for banning users or moderating posts with that adds a dropdown of common reasons and a textbox for further elaboration when appropriate - then makes this information appear on /moderation.&lt;/p&gt;&lt;h3&gt;Conclusions&lt;/h3&gt;&lt;p&gt;I should again emphasize that most moderator actions are benign and agreeable. They do a great job on the whole, but striving to do even better would be admirable. I suggest a few changes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Make a public audit log of moderation activity, or at least reach out to me to see what small changes could be done to help improve my statistics gathering.&lt;/li&gt;&lt;li&gt;Minimize use of more subtle actions like rank influence, and when used,&lt;/li&gt;&lt;li&gt;More frequently leave comments on posts where moderation has occurred explaining the rationale and opening an avenue for public discussion and/or appeal.&lt;/li&gt;&lt;li&gt;Put flagged posts into a queue for moderator review and don’t remove posts simply because they’re flagged.&lt;/li&gt;&lt;li&gt;Consider appointing one or two moderators from the community, ideally people with less bias towards SV or startup culture.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Hacker News is a great place for just that - hacker news. It has been for a long time and I hope it continues to be. Let’s work together on running it transparently to the benefit of all.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Analyzing-HN/</link>
        
        <pubDate>Wed, 13 Sep 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Analyzing-HN/</guid>
      </item>
    
      <item>
        
        
          <title>Killing ants with nuclear weapons</title>
          <description>
            &lt;p&gt;Complexity is quickly becoming an epidemic. In this developer’s opinion, complexity is the ultimate enemy - the final boss - of good software design. Complicated software generally has complicated bugs. Simple software generally has simple bugs. It’s as easy as that.&lt;/p&gt;&lt;p&gt;It’s for this reason that I strongly dislike many of the tools and architectures that have been proliferating over the past few years, particularly in web development. When I look at a tool like Gulp, I wonder if its success is largely attributable to people not bothering to learn how Makefiles work. Tools like Docker make me wonder if they’re an excuse to avoid learning how to do ops or how to use your distribution’s package manager. Chef makes me wonder if its users forgot that shell scripts can use SSH, too.&lt;/p&gt;&lt;p&gt;These tools offer a value add. But how does it compare to the cost of the additional complexity? In my opinion, in &lt;em&gt;every case&lt;/em&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; the value add is far outweighed by the massive complexity cost. This complexity cost shows itself when the system breaks (and it will - all systems break) and you have to dive into these overengineered tools. Don’t forget that dependencies are fallible, and never add a dependency you wouldn’t feel comfortable debugging. The time spent learning these complicated systems to fix the inevitable bugs is surely much less than the time spent learning the venerable tools that fill the same niche (or, in many cases, accepting that you don’t even need this particular shiny thing).&lt;/p&gt;&lt;p&gt;Reinventing the wheel is a favorite pastime of mine. There are many such wheels that I have reinvented or am currently reinventing. The problem isn’t in reinventing the wheel - it’s in doing so before you actually understand the wheel&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. I wonder if many of these complicated tools are written by people who set out before they fully understood what they were replacing, and I’m &lt;em&gt;certain&lt;/em&gt; that they’re mostly used by such people. I understand it may seem intimidating to learn venerable tools like make(1) or chroot(1), but they’re just a short man page away&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;It’s not just tools, though. I couldn’t explain the features of C++ in fewer than several thousand words (same goes for Rust). GNU continues to add proprietary extensions and unnecessary features to everything they work on. Every update shipped to your phone is making it slower to ensure you’ll buy the new one. Desktop applications are shipping entire web browsers into your disk and your RAM; server applications ship entire operating systems in glorified chroots; and hundreds of megabytes of JavaScript, ads, and spyware are shoved down the pipe on every web page you visit.&lt;/p&gt;&lt;p&gt;This is an epidemic. It’s time we cut this shit out. Please, design your systems with simplicity in mind. Moore’s law is running out&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, the free lunch is coming to an end. We have heaps and heaps of complicated, fragile abstractions to dismantle.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Complicated/</link>
        
        <pubDate>Fri, 08 Sep 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Complicated/</guid>
      </item>
    
      <item>
        
        
          <title>When not to use a regex</title>
          <description>
            &lt;p&gt;The other day, I saw &lt;a href=&quot;https://github.com/zeeshanu/learn-regex&quot; target=&quot;_blank&quot;&gt;Learn regex the easy way&lt;/a&gt;. This is a great resource, but I felt the need to pen a post explaining that regexes are usually not the right approach.&lt;/p&gt;&lt;p&gt;Let’s do a little exercise. I googled “URL regex” and here’s the first Stack Overflow result:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&amp;amp;//=]*)
&lt;/code&gt;&lt;/pre&gt;&lt;p style=&quot;text-align: right&quot;&gt;
&lt;small&gt;&lt;a href=&quot;https://stackoverflow.com/a/3809435/1191610&quot;&gt;source&lt;/a&gt;&lt;/small&gt;
&lt;/p&gt;
&lt;p&gt;This is a bad regex. Here are some valid URLs that this regex fails to match:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://x.org&quot; target=&quot;_blank&quot;&gt;http://x.org&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://nic.science&quot; target=&quot;_blank&quot;&gt;http://nic.science&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://名がドメイン.com&quot; target=&quot;_blank&quot;&gt;http://名がドメイン.com&lt;/a&gt; (warning: this is a parked domain)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://example.org/url,with,commas&quot; target=&quot;_blank&quot;&gt;http://example.org/url,with,commas&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Harry_Potter_(film_series)&quot; target=&quot;_blank&quot;&gt;https://en.wikipedia.org/wiki/Harry_Potter_(film_series)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://127.0.0.1&quot; target=&quot;_blank&quot;&gt;http://127.0.0.1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;http://[::1] (ipv6 loopback)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Here are some invalid URLs the regex is fine with:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://exam..ple.org&quot; target=&quot;_blank&quot;&gt;http://exam..ple.org&lt;/a&gt;&lt;/li&gt;&lt;li&gt;http://–example.org&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This answer has been revised 9 times on Stack Overflow, and this is the best they could come up with. Go back and read the regex. Can you tell where each of these bugs are? How long did it take you? If you received a bug report in your application because one of these URLs was handled incorrectly, do you understand this regex well enough to fix it? If your application has a URL regex, go find it and see how it fares with these tests.&lt;/p&gt;&lt;p&gt;Complicated regexes are opaque, unmaintainable, and often wrong. The correct approach to validating a URL is as follows:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;urllib&lt;/span&gt;.&lt;span class=&quot;constructor constant variable&quot;&gt;parse&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;urlparse&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;constructor constant variable function&quot;&gt;is_url_valid&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;url&lt;/span&gt;):
    &lt;span class=&quot;keyword&quot;&gt;try&lt;/span&gt;:
        &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;urlparse&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;url&lt;/span&gt;)
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;except&lt;/span&gt;:
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A regex is useful for validating &lt;em&gt;simple&lt;/em&gt; patterns and for &lt;em&gt;finding&lt;/em&gt; patterns in text. For anything beyond that it’s almost certainly a terrible choice. Say you want to…&lt;/p&gt;&lt;p&gt;&lt;strong&gt;validate an email address&lt;/strong&gt;: try to send an email to it!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;validate password strength requirements&lt;/strong&gt;: estimate the complexity with &lt;a href=&quot;https://github.com/dropbox/zxcvbn&quot; target=&quot;_blank&quot;&gt;zxcvbn&lt;/a&gt;!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;validate a date&lt;/strong&gt;: use your standard library! &lt;a href=&quot;https://docs.python.org/3.6/library/datetime.html#datetime.datetime.strptime&quot; target=&quot;_blank&quot;&gt;datetime.datetime.strptime&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;validate a credit card number&lt;/strong&gt;: run the &lt;a href=&quot;https://en.wikipedia.org/wiki/Luhn_algorithm&quot; target=&quot;_blank&quot;&gt;Luhn algorithm&lt;/a&gt; on it!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;validate a social security number&lt;/strong&gt;: alright, use a regex. But don’t expect the number to be assigned to someone until you ask the Social Security Administration about it!&lt;/p&gt;&lt;p&gt;Get the picture?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/When-not-to-use-a-regex/</link>
        
        <pubDate>Sun, 13 Aug 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/When-not-to-use-a-regex/</guid>
      </item>
    
      <item>
        
        
          <title>State of Sway August 2017</title>
          <description>
            &lt;p&gt;Is it already time to write another one of these? Phew, time flies. Sway marches ever forward. Sway 0.14.0 was recently released, adding much asked-after support for tray icons and fixing some long-standing bugs. As usual, we already have some exciting features slated for 0.15.0 as well, notably some cool improvements to clipboard support. Look forward to it!&lt;/p&gt;&lt;p&gt;Today Sway has 24,123 lines of C (and 4,489 lines of header files) written by 94 authors across 2,345 commits. These were written through 689 pull requests and 624 issues. Sway packages are available today in the repos of almost every Linux distribution.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/ICd5.png&quot;&gt;&lt;/p&gt;&lt;p&gt;For those who are new to the project, &lt;a href=&quot;http://swaywm.org&quot; target=&quot;_blank&quot;&gt;Sway&lt;/a&gt; is an i3-compatible Wayland compositor. That is, your existing &lt;a href=&quot;http://i3wm.org/&quot; target=&quot;_blank&quot;&gt;i3&lt;/a&gt; configuration file will work as-is on Sway, and your keybindings and colors and fonts and for_window rules and so on will all be the same. It’s i3, but for Wayland, plus it’s got some bonus features. Here’s a quick rundown of what’s new since the &lt;a href=&quot;https://drewdevault.com/blog/State-of-sway-April-2017/&quot;&gt;previous state of Sway&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Initial support for tray icons&lt;/li&gt;&lt;li&gt;X11/Wayland clipboard synchronization&lt;/li&gt;&lt;li&gt;nvidia proprietary driver support*&lt;/li&gt;&lt;li&gt;i3’s marks&lt;/li&gt;&lt;li&gt;i3’s mouse button bindings&lt;/li&gt;&lt;li&gt;Lots of i3 compatibility improvements&lt;/li&gt;&lt;li&gt;Lots of documentation improvements&lt;/li&gt;&lt;li&gt;Lots of bugfixes&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If this seems like a shorter list than usual, it’s because we’ve also been making great progress on wlroots too - no doubt thanks to the help of the many contributors doing amazing work in there. For those unaware, wlroots is our project to replace wlc with a new set of libraries for Wayland compositor underpinnings (it fills a similar niche as libweston). We now have a working DRM backend (including output rotation and hardware cursors) and libinput backend (including touchscreen and drawing tablet support), and we’re making headway now on drawing Wayland clients on screen.  I’m very excited about our pace and direction - keep an eye on it &lt;a href=&quot;https://github.com/SirCmpwn/wlroots/issues/9&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. I have also taken over for Cloudef as the maintainer of &lt;a href=&quot;https://github.com/Cloudef/wlc&quot; target=&quot;_blank&quot;&gt;wlc&lt;/a&gt; during the transition.&lt;/p&gt;&lt;p&gt;In other news, our bounty program continues to go strong. Our &lt;a href=&quot;https://github.com/SirCmpwn/sway/issues/986&quot; target=&quot;_blank&quot;&gt;current pot&lt;/a&gt; is $1200 and we’ve paid out $80 so far (and a $280 payout is on the horizon for tray icons). I’ve also started a &lt;a href=&quot;https://www.patreon.com/sircmpwn&quot; target=&quot;_blank&quot;&gt;Patreon page&lt;/a&gt;, where 26 patrons are generously supporting my work as maintainer of Sway and other projects. Many thanks to everyone who has contributed financially to Sway’s success!&lt;/p&gt;&lt;p&gt;That wraps up today’s post. Thanks for using Sway!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/State-of-Sway-August-2017/</link>
        
        <pubDate>Wed, 09 Aug 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/State-of-Sway-August-2017/</guid>
      </item>
    
      <item>
        
        
          <title>Archive it or you will miss it</title>
          <description>
            &lt;p&gt;Let’s open with some quotes from the &lt;a href=&quot;https://en.wikipedia.org/wiki/Link_rot&quot; target=&quot;_blank&quot;&gt;Wikipedia article on link rot&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;In 2014, bookmarking site Pinboard’s owner Maciej Cegłowski reported a “pretty steady rate” of 5% link rot per year… approximately 50% of the URLs in U.S. Supreme Court opinions no longer link to the original information… (analysis of) more than 180,000 links from references in… three major open access publishers… found that overall 24.5% of links cited were no longer available.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I hate link rot. It’s been common when servers disappeared or domains expired, in the past and still today. Today, link rot is on the rise under the influence of more sinister factors. Abuse of DMCA. Region locking. Paywalls.  Maybe it just no longer serves the interests of a walled garden to host the content. Maybe the walled garden went out of business. Users rely on platforms to host content and links rot by the millions when the platforms die. Movies disappear from Netflix.  Music vanishes from Spotify. Accounts are banned from SoundCloud. YouTube channels are banned over false DMCA requests issued by robots.&lt;/p&gt;&lt;p&gt;At this point, link rot is an axiom of the internet. In the face of this, I store a personal offline archive of &lt;em&gt;anything&lt;/em&gt; I want to see twice. When I see a cool YouTube video I like, I archive the entire channel right away. Rather than subscribe to it, I update my archive on a cronjob. I scrape content out of RSS feeds and into offline storage and I have dozens of websites archived with wget. I mirror most git repositories I’m interested in. I have DRM free offline copies of all of my music, TV shows, and movies, ill-begotten or not.&lt;/p&gt;&lt;p&gt;I suggest you do the same. It’s sad that it’s come to this. Let’s all do ourselves a favor. Don’t build unsustainable platforms and ask users to trust you with their data. Pay for your domain. Give people DRM free downloads. Don’t cripple your software when it can’t call home. If you run a website, let archive.org scrape it.&lt;/p&gt;&lt;p&gt;And archive anything you want to see again.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;0 0 * * 0 cd ~/archives &amp;amp;&amp;amp; wget -m https://drewdevault.com
&lt;/code&gt;&lt;/pre&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Archive-it-or-miss-it/</link>
        
        <pubDate>Mon, 19 Jun 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Archive-it-or-miss-it/</guid>
      </item>
    
      <item>
        
        
          <title>An introduction to Wayland</title>
          <description>
            &lt;p&gt;Wayland is the new hotness on the Linux graphics stack. There are plenty of introductions to Wayland that give you the high level details on how the stack is laid out how applications talk directly to the kernel with EGL and so on, but that doesn’t give you much practical knowledge. I’d like to instead share with you details about how the protocol actually works and how you can use it.&lt;/p&gt;&lt;p&gt;Let’s set aside the idea that Wayland has anything to do with graphics. Instead we’ll treat it like a generic protocol for two parties to share and talk about resources. These resources are at the heart of the Wayland protocol - resources like a keyboard or a surface to draw on. Each of these resources exposes an API for engaging with it, including functions you can call and &lt;em&gt;events&lt;/em&gt; you can listen to.&lt;/p&gt;&lt;p&gt;Some of these resources are &lt;em&gt;globals&lt;/em&gt;, which are exactly what they sound like. These resources include things like &lt;strong&gt;wl_outputs&lt;/strong&gt;, which are the displays connected to your graphics card. Other resources, like &lt;strong&gt;wl_surface&lt;/strong&gt;, require the client to ask the server to allocate new resources when needed. Negotiating for new resources is generally possible through the API of some global resource.&lt;/p&gt;&lt;p&gt;Your Wayland client gets started by obtaining a reference to the &lt;strong&gt;wl_display&lt;/strong&gt; like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wl_display_connect&lt;/span&gt;(&lt;span class=&quot;constant&quot;&gt;NULL&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This establishes a connection to the Wayland server. The most important role of the display, from the client perspective, is to provide the &lt;strong&gt;wl_registry&lt;/strong&gt;. The registry enumerates the globals available on the server.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_registry&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;registry&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wl_display_get_registry&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;display&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The registry emits an &lt;em&gt;event&lt;/em&gt; every time the server adds or removes a global. &lt;em&gt;Listening&lt;/em&gt; to these events is done by providing an implementation of a &lt;strong&gt;wl_registry_listener&lt;/strong&gt;, like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;global_add&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;our_data&lt;/span&gt;,
        &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_registry&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;registry&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;,
        &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;interface&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;version&lt;/span&gt;) {
    &lt;span class=&quot;comment&quot;&gt;// TODO&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;global_remove&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;our_data&lt;/span&gt;,
        &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_registry&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;registry&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;) {
    &lt;span class=&quot;comment&quot;&gt;// TODO&lt;/span&gt;
}

&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_registry_listener&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;registry_listener&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; {
    &lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;global_add&lt;/span&gt;,
    &lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;global_remove&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;global_remove&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Interfaces like this are used to listen to events from all kinds of resources. Attaching the listener to the registry is done like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;our_data&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;/* arbitrary state you want to keep around */&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;wl_registry_add_listener&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;registry&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;registry_listener&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;our_data&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;wl_display_dispatch&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;display&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;During the &lt;code&gt;wl_display_dispatch&lt;/code&gt;, the &lt;code&gt;global_add&lt;/code&gt; function is called for each global on the server. Subsequent calls to &lt;code&gt;wl_display_dispatch&lt;/code&gt; may call &lt;code&gt;global_remove&lt;/code&gt; when the server destroys globals. The &lt;code&gt;name&lt;/code&gt; passed into &lt;code&gt;global_add&lt;/code&gt; is more like an ID, and identifies this resource. The &lt;code&gt;interface&lt;/code&gt; tells you what API the resource implements, and distinguishes things like a &lt;strong&gt;wl_output&lt;/strong&gt; from a &lt;strong&gt;wl_seat&lt;/strong&gt;. The API these resources implement are described with XML files like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;xml&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string_special&quot;&gt;UTF-8&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;&amp;lt;!-- For copyright information, see https://git.io/vHyIB --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;gamma_control&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;gamma_control_manager&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;destructor&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;get_gamma_control&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;new_id&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;gamma_control&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;wl_output&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;gamma_control&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;invalid_gamma&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;destructor&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;set_gamma&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;reset_gamma&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;gamma_size&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;string punctuation_delimiter&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;markup&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A typical Wayland server implementing this protocol would create a &lt;code&gt;gamma_control_manager&lt;/code&gt; global and add it to the registry. The client then binds to this interface in our &lt;code&gt;global_add&lt;/code&gt; function like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;wayland-gamma-control-client-protocol.h&amp;quot;&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_output&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;// gamma_control_manager.name is a constant: &amp;quot;gamma_control_manager&amp;quot;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable function&quot;&gt;strcmp&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;interface&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;gamma_control_manager&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) {
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;gamma_control_manager&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mgr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;constant variable function&quot;&gt;wl_registry_bind&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;registry&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;name&lt;/span&gt;,
            &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;gamma_control_manager_interface&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;version&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;gamma_control&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;control&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;constant variable function&quot;&gt;gamma_control_manager_get_gamma_control&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;mgr&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;example&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable function&quot;&gt;gamma_control_set_gamma&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;control&lt;/span&gt;, &lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These functions are generated by running the XML file through &lt;code&gt;wayland-scanner&lt;/code&gt;, which outputs a header and C glue code. These XML files are called “protocol extensions” and let you add arbitrary extensions to the protocol. The core Wayland protocols themselves are described with similar XML files.&lt;/p&gt;&lt;p&gt;Using the Wayland protocol to create a surface to display pixels with consists of these steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Obtain a &lt;strong&gt;wl_display&lt;/strong&gt; and use it to obtain a &lt;strong&gt;wl_registry&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Scan the registry for globals and grab a &lt;strong&gt;wl_compositor&lt;/strong&gt; and a &lt;strong&gt;wl_shm_pool&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Use the &lt;strong&gt;wl_compositor&lt;/strong&gt; interface to create a &lt;strong&gt;wl_surface&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Use the &lt;strong&gt;wl_shell&lt;/strong&gt; interface to describe your surface’s role.&lt;/li&gt;&lt;li&gt;Use the &lt;strong&gt;wl_shm&lt;/strong&gt; interface to allocate shared memory to store pixels in.&lt;/li&gt;&lt;li&gt;Draw something into your shared memory buffers.&lt;/li&gt;&lt;li&gt;Attach your shared memory buffers to the &lt;strong&gt;wl_surface&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Let’s break this down.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;wl_compositor&lt;/strong&gt; provides an interface for interacting with the &lt;em&gt;compositor&lt;/em&gt;, that is the part of the Wayland server that &lt;em&gt;composites&lt;/em&gt; surfaces onto the screen. It’s responsible for creating surface resources for clients to use via &lt;code&gt;wl_compositor_create_surface&lt;/code&gt;. This creates a &lt;strong&gt;wl_surface&lt;/strong&gt; resource, which you can attach pixels to for the compositor to render.&lt;/p&gt;&lt;p&gt;The role of a surface is undefined by default - it’s just a place to put pixels. In order to get the compositor to do anything with them, you must give the surface a &lt;em&gt;role&lt;/em&gt;. Roles could be anything - desktop background, system tray, etc - but the most common role is a &lt;em&gt;shell surface&lt;/em&gt;. To create these, you take your wl_surface and hand it to the &lt;strong&gt;wl_shell&lt;/strong&gt; interface. You’ll get back a &lt;strong&gt;wl_shell_surface&lt;/strong&gt; resource, which defines your surface’s purpose and gives you an interface to do things like set the window title.&lt;/p&gt;&lt;p&gt;Attaching pixel buffers to a wl_surface is pretty straightforward. There are two primary ways of creating a buffer that both you and the compositor can use: EGL and shared memory. EGL lets you use an OpenGL context that renders directly on the GPU with minimal compositor involvement (fast) and shared memory (via &lt;strong&gt;wl_shm&lt;/strong&gt;) allows you to simply dump pixels in memory and hand them to the compositor (flexible). There are many other Wayland interfaces I haven’t covered, giving you everything from input devices (via &lt;strong&gt;wl_seat&lt;/strong&gt;) to clipboard access (via &lt;strong&gt;wl_data_source&lt;/strong&gt;), plus many protocol extensions. Learning more about these is an exercise left to the reader.&lt;/p&gt;&lt;p&gt;Before we wrap this article up, let’s take a brief moment to discuss the server. Most of the concepts here are already familiar to you by now. The Wayland server also utilizes a &lt;strong&gt;wl_display&lt;/strong&gt;, but differently from the client. The display on the server has ownership over the &lt;em&gt;event loop&lt;/em&gt;, via &lt;strong&gt;wl_event_loop&lt;/strong&gt;. The event loop of a Wayland server might look like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;display&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wl_display_create&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_event_loop&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;event_loop&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wl_display_get_event_loop&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;display&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (true) {
    &lt;span class=&quot;constant variable function&quot;&gt;wl_event_loop_dispatch&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;event_loop&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The event loop has a lot of helpful utilities for the Wayland server to take advantage of, including internal event sources, timers, and file descriptor monitoring. Before starting the event loop the server is going to start obtaining its own resources and creating Wayland globals for them with &lt;code&gt;wl_global_create&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_global&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wl_global_create&lt;/span&gt;(
    &lt;span class=&quot;constant variable&quot;&gt;display&lt;/span&gt;,
    &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;wl_output_interface&lt;/span&gt;,
    &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;/* version */&lt;/span&gt;,
    &lt;span class=&quot;constant variable&quot;&gt;our_data&lt;/span&gt;,
    &lt;span class=&quot;constant variable&quot;&gt;wl_output_bind&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;wl_output_bind&lt;/code&gt; function here is going to be called when a client attempts to bind to this resource via &lt;code&gt;wl_registry_bind&lt;/code&gt;, and will look something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wl_output_bind&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_client&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;our_data&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;their_version&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;id&lt;/span&gt;) {
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wl_resource&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;constant variable function&quot;&gt;wl_resource_create_checked&lt;/span&gt;(
            &lt;span class=&quot;constant variable&quot;&gt;client&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;wl_output_interface&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;their_version&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;our_version&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;id&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// ...send output modes or whatever else you need to do&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some of the resources a server is going to be managing might include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;DRM state for direct access to outputs&lt;/li&gt;&lt;li&gt;GLES context (or another GL implementation) for rendering&lt;/li&gt;&lt;li&gt;libinput for input devices&lt;/li&gt;&lt;li&gt;udev for hotplugging&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Through the Wayland protocol, the server provides an abstraction on top of these resources and offers them to clients. Some servers go further, with novel ways of compositing clients or handling input. Some provide additional interactivity, such as desktop shells that are actually running in the compositor rather than external clients. Other servers are designed for mobile use and provide a user experience that more closely matches the mobile experience than the traditional desktop experience. Wayland is designed to be flexible!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Introduction-to-Wayland/</link>
        
        <pubDate>Sat, 10 Jun 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Introduction-to-Wayland/</guid>
      </item>
    
      <item>
        
        
          <title>Limited &quot;generics&quot; in C without macros or UB</title>
          <description>
            &lt;p&gt;I should start this post off by clarifying that what I have to show you today is not, in fact, generics. However, it’s useful in some situations to solve the same problems that generics might. This is a pattern I’ve started using to reduce the number of &lt;code&gt;void*&lt;/code&gt; pointers floating around in my code: multiple definitions of a struct.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Errata&lt;/strong&gt;: we rolled this approach back in wlroots because it causes problems with LTO. I no longer recommend it.&lt;/p&gt;&lt;p&gt;Let’s take a look at a specific example. In &lt;a href=&quot;https://github.com/SirCmpwn/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;, &lt;code&gt;wlr_output&lt;/code&gt; is a generic type that can be implemented by any number of backends, like DRM (direct rendering manager), wayland windows, X11 windows, RDP outputs, etc. The &lt;code&gt;wlr/types.h&lt;/code&gt; header includes this structure:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_impl&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_state&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output&lt;/span&gt; {
    &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_impl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// [...]&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wlr_output_enable&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;enable&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wlr_output_set_mode&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;,
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_mode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mode&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wlr_output_destroy&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;wlr_output_impl&lt;/code&gt; is defined elsewhere:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_impl&lt;/span&gt; {
    &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; (&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;enable&lt;/span&gt;)(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;enable&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;bool&lt;/span&gt; (&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;set_mode&lt;/span&gt;)(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;,
        &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_mode&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mode&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; (&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;destroy&lt;/span&gt;)(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable function&quot;&gt;wlr_output_create&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_impl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;impl&lt;/span&gt;,
        &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wlr_output_free&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nowhere, however, is &lt;code&gt;wlr_output_state&lt;/code&gt; defined. It’s left an incomplete type throughout all of the common &lt;code&gt;wlr_output&lt;/code&gt; code. The “generic” part is that each output implementation, in its own private headers, defines the &lt;code&gt;wlr_output_state&lt;/code&gt; struct for itself, like the DRM backend:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_state&lt;/span&gt; {
    &lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;connector&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;name&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;crtc&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;drmModeCrtc&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;old_crtc&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_drm_renderer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;renderer&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;gbm_surface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;gbm&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;EGLSurface&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;egl&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;pageflip_pending&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_drm_output_state&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// [...]&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows implementations of the &lt;code&gt;enable&lt;/code&gt;, &lt;code&gt;set_mode&lt;/code&gt;, and &lt;code&gt;destroy&lt;/code&gt; functions to avoid casting a &lt;code&gt;void*&lt;/code&gt; to the appropriate type:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_impl&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;output_impl&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; {
    &lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;wlr_drm_output_enable&lt;/span&gt;,
    &lt;span class=&quot;comment&quot;&gt;// [...]&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wlr_drm_output_enable&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;enable&lt;/span&gt;) {
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_backend_state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;constant variable function&quot;&gt;wl_container_of&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;renderer&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;renderer&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;DRM_OUTPUT_CONNECTED&lt;/span&gt;) {
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;enable&lt;/span&gt;) {
        &lt;span class=&quot;constant variable function&quot;&gt;drmModeConnectorSetProperty&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;connector&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;dpms&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;DRM_MODE_DPMS_ON&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;comment&quot;&gt;// [...]&lt;/span&gt;
    } &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt; {
        &lt;span class=&quot;constant variable function&quot;&gt;drmModeConnectorSetProperty&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;fd&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;connector&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;dpms&lt;/span&gt;,
            &lt;span class=&quot;constant variable&quot;&gt;DRM_MODE_DPMS_STANDBY&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
}

&lt;span class=&quot;comment&quot;&gt;// [...]&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;wlr_output&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;wlr_output_create&lt;/span&gt;(&lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;output_impl&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;output&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The limitations of this approach are apparent: you cannot work with multiple definitions of &lt;code&gt;wlr_output_state&lt;/code&gt; in the same file. However, you get improved type safety, have to write less code, and improve readability.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Limited-generics-in-C/</link>
        
        <pubDate>Mon, 05 Jun 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Limited-generics-in-C/</guid>
      </item>
    
      <item>
        
        
          <title>Rotating passwords in bulk in the wake of security events</title>
          <description>
            &lt;p&gt;I’ve been putting this post off for a while. Do you remember the &lt;a href=&quot;https://blog.cloudflare.com/incident-report-on-memory-leak-caused-by-cloudflare-parser-bug/&quot; target=&quot;_blank&quot;&gt;CloudFlare security problem&lt;/a&gt; that happened a few months ago? This is the one that disclosed huge amounts of sensitive information for huge numbers websites. When this happened, your accounts on &lt;a href=&quot;https://github.com/pirate/sites-using-cloudflare&quot; target=&quot;_blank&quot;&gt;thousands of websites&lt;/a&gt; were potentially compromised.&lt;/p&gt;&lt;p&gt;Updating passwords for all of these services at once was a major source of frustration for users. Updating a single password can take 5 minutes, and changing dozens of them might take hours. I decided that I wanted to make this process easier.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ ./pass-rotate github.com linode.com news.ycombinator.com twitter.com
Rotating github.com... 
  Enter your two factor (TOTP) code:
OK
Rotating linode.com... 
  Enter your two-factor (TOTP) code:
OK
Rotating news.ycombinator.com... OK
Rotating twitter.com... 
  Enter your SMS authorization code:
OK                                                                       
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I just changed 4 passwords in about 20 seconds. This is &lt;a href=&quot;https://github.com/SirCmpwn/pass-rotate&quot; target=&quot;_blank&quot;&gt;pass-rotate&lt;/a&gt;, which is basically youtube-dl for rotating passwords. It integrates with your password manager to make it easy to change your password. pass-rotate is also provided in the form of a library that password managers can directly integrate with to provide first-class support for password rotation with a shared implementation of various websites. Not only can it help you rotate passwords after security events, but it can be used for periodic password rotation to keep your accounts safer in general.&lt;/p&gt;&lt;p&gt;How this was basically done is by reverse engineering the password change flow of each of the websites it supports. Each provider’s backend submits HTTP requests that simulates logging into the website and interacting with the password reset form. This is often quite simple, like &lt;a href=&quot;https://github.com/SirCmpwn/pass-rotate/blob/master/passrotate/providers/github.py&quot; target=&quot;_blank&quot;&gt;github.py&lt;/a&gt;, but can sometimes be quite complex, like &lt;a href=&quot;https://github.com/SirCmpwn/pass-rotate/blob/master/passrotate/providers/namecheap.py&quot; target=&quot;_blank&quot;&gt;namecheap.py&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The current list of supported services is available &lt;a href=&quot;https://github.com/SirCmpwn/pass-rotate/wiki/Currently-supported-services&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. There’s also an issue to discuss making a standardized mechanism for automated password rotation &lt;a href=&quot;https://github.com/SirCmpwn/pass-rotate/issues/1&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. At the time of writing, the list of supported services is:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cloudflare &lt;sub&gt;✗ TOTP&lt;/sub&gt;&lt;/li&gt;&lt;li&gt;Digital Ocean &lt;sub&gt;✗ TOTP&lt;/sub&gt;&lt;/li&gt;&lt;li&gt;Discord &lt;sub&gt;✓ TOTP&lt;/sub&gt;&lt;/li&gt;&lt;li&gt;GitHub &lt;sub&gt;✓ TOTP ✗ U2F&lt;/sub&gt;&lt;/li&gt;&lt;li&gt;Linode &lt;sub&gt;✓ TOTP&lt;/sub&gt;&lt;/li&gt;&lt;li&gt;NameCheap &lt;sub&gt;✓ SMS&lt;/sub&gt;&lt;/li&gt;&lt;li&gt;Pixiv&lt;/li&gt;&lt;li&gt;Twitter &lt;sub&gt;✓ SMS ✓ TOTP&lt;/sub&gt;&lt;/li&gt;&lt;li&gt;YCombinator&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Adding new services is easy - check out &lt;a href=&quot;https://github.com/SirCmpwn/pass-rotate/blob/master/CONTRIBUTING.md&quot; target=&quot;_blank&quot;&gt;the guide&lt;/a&gt;. I would be happy to merge your pull requests. Please add websites you use and websites you maintain!&lt;/p&gt;&lt;p&gt;I also set up a Patreon campaign today. If you’d like to contribute to my work, please visit &lt;a href=&quot;https://patreon.com/sircmpwn&quot; target=&quot;_blank&quot;&gt;the Patreon page&lt;/a&gt;. This supports all of my open source projects, but if you want to support pass-rotate in particular feel free to let me know when you make your contribution. This kind of project needs long term maintenance to support countless providers and keep up with changes to them. Feel free to let me know what service providers you want me to add support for when you make your pledge!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Rotating-passwords/</link>
        
        <pubDate>Thu, 11 May 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Rotating-passwords/</guid>
      </item>
    
      <item>
        
        
          <title>Building a &quot;real&quot; Linux distro</title>
          <description>
            &lt;p&gt;I recently saw a post on Hacker News: “&lt;a href=&quot;https://github.com/MichielDerhaeg/build-linux&quot; target=&quot;_blank&quot;&gt;Build yourself a Linux&lt;/a&gt;”, a cool project that guides you through building a simple Linux system. It’s similar to Linux from Scratch in that it helps you build a simple Linux system for personal use. I’d like to supplement this with some insight into my experience with a more difficult task: building a full blown Linux distribution. The result is &lt;a href=&quot;http://agunix.org&quot; target=&quot;_blank&quot;&gt;agunix&lt;/a&gt;, the “silver unix” system.&lt;/p&gt;&lt;p&gt;For many years I’ve been frustrated with every distribution I’ve tried. Many of them have compelling features and design, but there’s always a catch. The popular distros are stable and portable, but cons include bloat, frequent use of GNU, systemd, and often apt. Some more niche distros generally have good points but often have some combination of GNU, an init system I don’t like, poor docs, dynamic linking, or an overall amateurish or incomplete design. Many of them are tolerable, but none have completely aligned with my desires.&lt;/p&gt;&lt;p&gt;I’ve also looked at not-Linux - I have plenty of beefs with the Linux kernel. I like the BSD kernels, but I dislike the userspaces (though NetBSD is pretty good) I like the microkernel design of Minix, but it’s too unstable and has shit hardware support. plan9/9front has the most elegant kernel and userspace design ever made, but it’s not POSIX and has shit hardware support. Though none of these userspaces are for me, I intend to attempt a port of the agunix userspace to all of their kernels at some point (a KFreeBSD port is underway).&lt;/p&gt;&lt;p&gt;After trying a great number of distros and coming away with a kind of dissatisfaction unique to each one, I resolved to make a distro that embodied my own principles about userspace design. It turns out this is a ton of work - here’s how it’s done.&lt;/p&gt;&lt;p&gt;Let’s distinguish a Linux “system” from a Linux “distribution”. A Linux system is anything that boots up from the Linux kernel. A Linux &lt;em&gt;distribution&lt;/em&gt;, on the other hand, is a Linux system that can be &lt;em&gt;distributed&lt;/em&gt; to end users.  It’s this sort of system that I wanted to build. In my opinion, there are two core requirements for a Linux system to become a Linux distribution:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;It has a package manager (or some other way of staying up to date)&lt;/li&gt;&lt;li&gt;It is self-hosting (it can compile itself and all of the infrastructure runs on it)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The first order of business in creating a Linux distro is to fulfill these two requirements. Getting to this stage is called &lt;em&gt;bootstrapping&lt;/em&gt; your distribution - everything else can come later. To do this, you’ll need to port your package manager to your current system, and start building the base packages with it. If your new distro doesn’t use the same architecture or libc as your current system, you also need to build a cross compiler and use it for building your new packages.&lt;/p&gt;&lt;p&gt;My initial approach was different - I used my cross compiler to fill up a chroot with software without using my package manager, hoping to later bootstrap from it. I used this approach on my first 3 attempts before deciding to make base packages on the host system instead. With this approach, I started by building packages that weren’t necessarily self hosting - they used the host-specific cross compiler builds and such - but produced working packages for the new environment. I built packages for:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;my package manager&lt;/li&gt;&lt;li&gt;musl libc&lt;/li&gt;&lt;li&gt;bash&lt;/li&gt;&lt;li&gt;busybox&lt;/li&gt;&lt;li&gt;autotools&lt;/li&gt;&lt;li&gt;make&lt;/li&gt;&lt;li&gt;gcc (clang can’t compile the Linux kernel)&lt;/li&gt;&lt;li&gt;vim&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I also had to package all of the dependencies for these. Once I had a system that was reasonably capable of compiling arbitrary software, I transferred my PKGBUILDs (scripts used to build packages) to my chroot and started tweaking them to re-build packages from the new distro itself. This process took months to get completely right - there are &lt;em&gt;tons&lt;/em&gt; of edge cases and corner cases. Simply getting this software to run in a new Linux system is only moderately difficult - getting a system that can build itself is &lt;em&gt;much harder&lt;/em&gt;. I was successful on my 4th attempt, but threw it out and redid it to get a cleaner distribution with the benefit of hindsight. This became agunix.&lt;/p&gt;&lt;p&gt;Once you reach this stage you can go ham on making packages for your system. The next step for me was graduating from a chroot to dedicated hardware. I built out an init system with runit and &lt;a href=&quot;http://git.agunix.org/init/&quot; target=&quot;_blank&quot;&gt;agunix-init&lt;/a&gt; and various other packages that are useful on a proper install. I also compiled a kernel without support for loadable modules (on par with the static linking theme of agunix). If you make your own Linux distribution you will probably have to figure out modules yourself, likely implicating something like eudev. Eventually, I was able to get agunix &lt;a href=&quot;https://drewdevault.com/l.sr.ht/OzCq.jpg&quot;&gt;running on my laptop&lt;/a&gt;, which has now become my primary agunix dev machine (often via SSH from my dev desktop).&lt;/p&gt;&lt;p&gt;The next stage for me was getting agunix.org up and running on agunix. I deliberately chose not to have a website until it could be hosted on agunix itself. I deployed agunix to a VPS, then ported nginx and put the website up. The rest of the infrastructure was a bit more difficult: cgit took me about 10 packages of work, and bugzilla was about 100 packages of work. Haven’t started working on mailman yet.&lt;/p&gt;&lt;p&gt;Then begins the eternal packaging phase. At this point you’ve successfully made a Linux distribution, and now you just need to fill it with packages. This takes &lt;em&gt;forever&lt;/em&gt;. I have made 407 packages to date and I still don’t have a desktop to show for it (I’m &lt;em&gt;almost&lt;/em&gt; there, just have to make a few dozen more packages before &lt;a href=&quot;https://github.com/SirCmpwn/sway&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt; will run). At this point to have success you need others to buy into your ideas and start contributing - it’s impossible to package everything yourself. Speaking of which, check out &lt;a href=&quot;http://agunix.org&quot; target=&quot;_blank&quot;&gt;agunix.org&lt;/a&gt; and see if you like it! I haven’t been doing much marketing for this distro yet, but I do have a little bit of help. If you’re interested in contributing in a new distro, we have lots of work for you to do!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Building-a-real-Linux-distro/</link>
        
        <pubDate>Fri, 05 May 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Building-a-real-Linux-distro/</guid>
      </item>
    
      <item>
        
        
          <title>State of Sway April 2017</title>
          <description>
            &lt;p&gt;Development on Sway continues. I thought we would have slowed down a lot more by now, but every release still comes with new features - Sway 0.12 added redshift support and binary space partitioning layouts. Sway 0.13.0 is coming soon and includes, among other things, nvidia proprietary driver support. We already have some interesting features slated for Sway 0.14.0, too!&lt;/p&gt;&lt;p&gt;Today Sway has 21,446 lines of C (and 4,261 lines of header files) written by 81 authors across 2,263 commits. These were written through 653 pull requests and 529 issues. Sway packages are available today in the official repos of pretty much every distribution except for Debian derivatives, and a PPA is available for those guys.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/ICd5.png&quot;&gt;&lt;/p&gt;&lt;p&gt;For those who are new to the project, &lt;a href=&quot;http://swaywm.org&quot; target=&quot;_blank&quot;&gt;Sway&lt;/a&gt; is an i3-compatible Wayland compositor. That is, your existing &lt;a href=&quot;http://i3wm.org/&quot; target=&quot;_blank&quot;&gt;i3&lt;/a&gt; configuration file will work as-is on Sway, and your keybindings and colors and fonts and for_window rules and so on will all be the same. It’s i3, but for Wayland, plus it’s got some bonus features. Here’s a quick rundown of what’s new since the &lt;a href=&quot;https://drewdevault.com/blog/State-of-sway/&quot;&gt;previous state of Sway&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Redshift support&lt;/li&gt;&lt;li&gt;Improved security configuration&lt;/li&gt;&lt;li&gt;Automatic binary space partitioning layouts ala AwesomeWM&lt;/li&gt;&lt;li&gt;Support for more i3 window criterion&lt;/li&gt;&lt;li&gt;Support for i3 marks&lt;/li&gt;&lt;li&gt;xdg_shell v6 support (Wayland thing, makes more native Wayland programs work)&lt;/li&gt;&lt;li&gt;We’ve switched from X.Y to X.Y.Z releases, Z releases shipping bugfixes while the next Y release is under development&lt;/li&gt;&lt;li&gt;Lots of i3 compatibility improvements&lt;/li&gt;&lt;li&gt;Lots of documentation improvements&lt;/li&gt;&lt;li&gt;Lots of bugfixes&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The new &lt;a href=&quot;https://github.com/SirCmpwn/sway/issues/986&quot; target=&quot;_blank&quot;&gt;bounty program&lt;/a&gt; has also raised $1,200 to support Sway development! Several bounties have been awarded, including redshift support and i3 marks, but every awardee chose to redonate their reward to the bounty pool. Thanks to everyone who’s donated and everyone who’s worked on new features! Bounties have also been awarded for features in the Wayland ecosystem beyond Sway - a fact I’m especially proud of. If you want a piece of that $1,200 pot, &lt;a href=&quot;http://webchat.freenode.net/?channels=sway&amp;uio=d4&quot; target=&quot;_blank&quot;&gt;join us on IRC&lt;/a&gt; and we’ll help you get started.&lt;/p&gt;&lt;p&gt;Many new developments are in the pipeline for you. 0.13.0 is expected to ship within the next few weeks - &lt;a href=&quot;https://github.com/SirCmpwn/sway/issues/1162#issuecomment-295012255&quot; target=&quot;_blank&quot;&gt;here’s a sneak peek at the changelog&lt;/a&gt;. In the future releases, development is ongoing for tray icons (encouraged by the sweet $270 bounty sitting on that feature), and several other features for 0.14.0 have been completed. We’ve also started work on a long term project to replace our compositor plumbling library, wlc, with a new one: &lt;a href=&quot;https://github.com/SirCmpwn/wlroots&quot; target=&quot;_blank&quot;&gt;wlroots&lt;/a&gt;. This should allow us to fix many of the more difficult bugs in Sway, and opens the doors for &lt;em&gt;many&lt;/em&gt; features that weren’t previously possible. It should also give us a platform on which we can build standard protocols that other compositors can implement, unifying the Wayland platform a bit more.&lt;/p&gt;&lt;p&gt;Many thanks to &lt;a href=&quot;https://github.com/SirCmpwn/sway/graphs/contributors&quot; target=&quot;_blank&quot;&gt;everyone that’s contributed to sway&lt;/a&gt;! There’s no way Sway would have enjoyed its success without your help. That wraps things up for today, thanks for using Sway and look forward to Sway 1.0!&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Note: future posts like this will omit some of the stats that were included in the previous posts. You can use the following commands to find them for yourself:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# Lines of code per author:&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ls-tree&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--name-only&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;HEAD&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;*/*.c&lt;/span&gt; \
    &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant function&quot;&gt;xargs&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-0&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-n1&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;blame&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--line-porcelain&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;HEAD&lt;/span&gt; \
    &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant function&quot;&gt;grep&lt;/span&gt;  &lt;span class=&quot;string constant&quot;&gt;&amp;quot;^author &amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant function&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant function&quot;&gt;uniq&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant function&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-nr&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# Commits per author:&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;shortlog&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/State-of-sway-April-2017/</link>
        
        <pubDate>Sat, 29 Apr 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/State-of-sway-April-2017/</guid>
      </item>
    
      <item>
        
        
          <title>MSG_PEEK is pretty common, CVE-2016-10229 is worse than you think</title>
          <description>
            &lt;p&gt;I heard about &lt;a href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2016-10229&quot; target=&quot;_blank&quot;&gt;CVE-2016-10229&lt;/a&gt; earlier today. In a nutshell, it allows for arbitrary code execution via UDP traffic if userspace programs are using &lt;code&gt;MSG_PEEK&lt;/code&gt; in their &lt;code&gt;recv&lt;/code&gt; calls. I quickly updated my kernels and rebooted any boxes where necessary, but when I read the discussions on this matter I saw people downplaying this issue by claiming &lt;code&gt;MSG_PEEK&lt;/code&gt; is an obscure feature.&lt;/p&gt;&lt;p&gt;I don’t want to be a fear monger and I’m by no means a security expert but I suspect that this is a deeply incorrect conclusion. If I understand this vulnerability right you need to drop everything and update any servers running a kernel &lt;4.5 &lt;em&gt;immediately&lt;/em&gt;. &lt;code&gt;MSG_PEEK&lt;/code&gt; allows a programmer using UDP to read from the kernel’s UDP buffer without consuming the data (so subsequent reads will continue to read the same data). This immediately sounds to me like a pretty useful feature that a lot of software might use, not an obscure one.&lt;/p&gt;&lt;p&gt;I did quick search for software where &lt;code&gt;MSG_PEEK&lt;/code&gt; appears in the source code somewhere. This does not necessarily mean that it’s exploitable, but should certainly raise red flags. Here’s a list of some notable software I found:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;nginx&lt;/li&gt;&lt;li&gt;haproxy&lt;/li&gt;&lt;li&gt;curl&lt;/li&gt;&lt;li&gt;gnutls&lt;/li&gt;&lt;li&gt;jack2&lt;/li&gt;&lt;li&gt;lynx&lt;/li&gt;&lt;li&gt;plex (and kodi/xbmc)&lt;/li&gt;&lt;li&gt;busybox&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I also found a few things like programming languages and networking libraries that you might expect to have MSG_PEEK if only to provide that functionality to programmers leveraging them. I didn’t investigate too deeply into whether or not that was the case or if this software is using the feature in a less apparent way, but in this category I found Python, Ruby, Node.js, smalltalk, octave, libnl, and socat. I used searchcode.com to find these - &lt;a href=&quot;https://searchcode.com/?q=MSG_PEEK&quot; target=&quot;_blank&quot;&gt;here’s the full search results&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Again, I’m not a security expert, but I’m &lt;em&gt;definitely&lt;/em&gt; spooked enough to update my shit and I suggest you do so as well. Red Hat, Debian, and Ubuntu are all unaffected because of the kernel they ship. Note, however, that many cloud providers do not let you choose your own kernel. This could mean that you are affected even if you’re running a distribution like Debian. Double check it - use &lt;code&gt;uname -r&lt;/code&gt; and update+reboot if necessary.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/MSG_PEEK-is-more-common-than-you-think-CVE-2016-10229/</link>
        
        <pubDate>Thu, 13 Apr 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/MSG_PEEK-is-more-common-than-you-think-CVE-2016-10229/</guid>
      </item>
    
      <item>
        
        
          <title>Principles for C programming</title>
          <description>
            &lt;p&gt;In the words of Doug Gwyn, “Unix was not designed to stop you from doing stupid things, because that would also stop you from doing clever things”. C is a very powerful tool, but it is to be used with care and discipline. Learning this discipline is well worth the effort, because C is one of the best programming languages ever made. A disciplined C programmer will…&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Prefer maintainability&lt;/strong&gt;. Do not be clever where cleverness is not required. Instead, seek out the simplest and most understandable solution that meets the requirements. Most concerns, including performance, are secondary to maintainability. You should have a performance budget for your code, and you should be comfortable spending it.&lt;/p&gt;&lt;p&gt;As you become more proficient with the language and learn about more features you can take advantage of, you should also be learning when not to use them. It’s more important that a novice could understand your code than it is to use some interesting way of solving the problem. Ideally, a novice will understand your code &lt;em&gt;and&lt;/em&gt; learn something from it. Write code as if the person maintaining it was you, circa last year.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Avoid magic&lt;/strong&gt;. Do not use macros&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Do not use a typedef to hide a pointer or avoid writing “struct”. Avoid writing complex abstractions. Keep your build system simple and transparent. Don’t use stupid hacky crap just because it’s a cool way of solving the problem. The underlying behavior of your code should be apparent even without context.&lt;/p&gt;&lt;p&gt;One of C’s greatest advantages is its transparency and simplicity. This should be embraced, not subverted. But in the fine C tradition of giving yourself enough rope to hang yourself with, you can use it for magical purposes. You must not do this. Be a muggle.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recognize and avoid dangerous patterns&lt;/strong&gt;. Do not use fixed size buffers with variable sized data - always calculate how much space you’ll need and allocate it. Read the man pages for functions you use and handle their failure modes. Immediately convert unsafe user input into sanitized C structures. If you later have to present this data to the user, keep it in C structures until the last possible moment. Learn of and use extra care around sensitive functions like strcat.&lt;/p&gt;&lt;p&gt;Writing C is sometimes like handling a gun. Guns are important tools, but accidents with them can be very bad. You treat guns with care: you don’t point them at anything you love, you exercise good trigger discipline, and you treat it like it’s always loaded. And like guns are useful for making holes in things, C is useful for writing kernels with.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Take care organizing the code&lt;/strong&gt;. Never put code into a header. Never use the &lt;code&gt;inline&lt;/code&gt; keyword. Put separate concerns in separate files. Use static functions liberally to organize your logic. Use a coding style that gives everything enough breathing room to be easy on the eyes. Use single letter variable names when their purpose is self-evident and descriptive names when it’s not, and avoid neither.&lt;/p&gt;&lt;p&gt;I like to organize my code into directories that implement some group of functions, and give each function its own file. This file will often contain lots of static functions, but they all serve to organize the behavior this file is responsible for implementing. Write up a header to give others access to this module. And use the Linux kernel coding style, god dammit.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Use only standard features&lt;/strong&gt;. Do not assume the platform is Linux. Do not assume the compiler is gcc. Do not assume the libc is glibc. Do not assume the architecture is x86. Do not assume the coreutils are GNU. Do not define _GNU_SOURCE.&lt;/p&gt;&lt;p&gt;If you must use platform-specific features, describe an interface for it, then write platform-specific support code separately. Under no circumstances should you ever use gcc extensions or glibc extensions. GNU is a blight on this Earth, do not let it infect your code.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Use a disciplined workflow&lt;/strong&gt;. Have a disciplined approach to version control, too. Write thoughtful commit messages - briefly explain the change in the first line, and add justification for it in the extended commit message. Work in feature branches with clearly defined goals, and do not include changes that don’t serve that goal. Do not be afraid to rebase and edit your branch’s history so that it presents your changes clearly.&lt;/p&gt;&lt;p&gt;When you have to return to your code later, you will be thankful for the detailed commit message you wrote. Others who interact with your code will be thankful for this as well. When you see some stupid code, it’s nice to know what the bastard was thinking at the time, especially when the bastard in question was you.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Do strict testing and reviews&lt;/strong&gt;. Identify the different possible code paths that your changes may take. Test each of them for the correct behavior. Give it incorrect input. Give it inputs that could “never happen”. Pay special attention to error-prone patterns. Look for places to simplify the code and make the processes clearer.&lt;/p&gt;&lt;p&gt;Next, give your changes to another human to review. This human should apply the same process and sign off on your changes. Review with discipline as well, taking all of the same steps. Review like it’ll be your ass on the line if there’s a problem with this code.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Learn from mistakes&lt;/strong&gt;. First, fix the bug. Then, fix the real bug: your process allowed this mistake to happen. Bring your code reviewer into the discussion - this is their fault, too. Critically examine the process of writing, reviewing, and deploying this code, and seek out the root cause.&lt;/p&gt;&lt;p&gt;The solution might be simple, like adding strcat to the list of functions that should trigger your “review this code carefully” reflex. It might be employing static analysis so a computer can detect this problem for you. Perhaps the code needs to be refactored so it’s simpler and easier to spot errors in. Failing to reflect on how to avoid future fuck-ups would be the real fuck-up here.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;It’s important to remember that rules are made to be broken. There may be cases where things that are discouraged should be used, and things that are encouraged disregarded. You should strive to make such cases the exception, not the norm, and carefully justify them when they happen.&lt;/p&gt;&lt;p&gt;C is the shit. I love it, and I hope more people can learn to see it the way I do. Good luck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-I-learned-to-stop-worrying-and-love-C/</link>
        
        <pubDate>Wed, 15 Mar 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-I-learned-to-stop-worrying-and-love-C/</guid>
      </item>
    
      <item>
        
        
          <title>Compiler devnotes: Machine specs</title>
          <description>
            &lt;p&gt;I have a number of long-term projects that I plan for on long timelines, on the order of decades or more. One of these projects is cozy, a C toolchain. I haven’t talked about this project in public before, so I’ll start by introducing you to the project. The main C toolchains in the “actually usable” category are GNU and LLVM, but I’m satisfied with neither and I want to build my own toolchain. I see no reason why compilers should be deep magic. Here are my goals for cozy:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Self hosting and written in C&lt;/li&gt;&lt;li&gt;An easy to grok codebase and internal design&lt;/li&gt;&lt;li&gt;Focused on C. No built-in support for other languages&lt;/li&gt;&lt;li&gt;Adding new targets architectures and ports should be straightforward&lt;/li&gt;&lt;li&gt;Modular build pipeline with lots of opportunities for external integrations&lt;/li&gt;&lt;li&gt;Trivially cross-compiles without building another version of the toolchain&lt;/li&gt;&lt;li&gt;Includes a decent optimizer&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Some other plans include opinionated warnings about code and minimal support for language extensions. Ambitious goals, right? That’s why this project is on my long-term schedule. I’ve found that large projects are entirely feasible, so long as you (1) start them and (2) keep working on them for a long time. I don’t need to rush this - gcc and clang may not be ideal, but they work today. In support of these goals, I’ll be writing these dev notes to explain my design choices and gather feedback — please &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email me&lt;/a&gt; if you have some!&lt;/p&gt;&lt;p&gt;Since I want to place an emphasis on portability and retargetability, I’m starting by designing the machine spec and its support code, which is used to add support for new architectures. I don’t like gcc’s lisp specs, and I &lt;em&gt;really&lt;/em&gt; don’t like LLVM’s “huge pile of C++” approach. I think a really good machine spec meets these goals:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Easy to write and human friendly&lt;/li&gt;&lt;li&gt;More about data than code, but&lt;/li&gt;&lt;li&gt;Easily extended with C to support architecture-specific nuances&lt;/li&gt;&lt;li&gt;Provides loads of useful metadata about the target architecture&lt;/li&gt;&lt;li&gt;Exposes information about the speed and side-effects of each instruction&lt;/li&gt;&lt;li&gt;Can also be used to generate an assembler and disassembler&lt;/li&gt;&lt;li&gt;Easily reused to create derivative architectures&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Adding a new architecture should be a weekend project, and when you’re done the entire toolchain should both support and run on your new architecture. I set out to come up with a new syntax that could potentially meet these goals. I started with the Z80 architecture in mind because it’s simple, I’m intimately familiar with it, and I want cozy to be able to target 8-bit machines just as easily as 32 or 64 bit.&lt;/p&gt;&lt;p&gt;For reference, here are the gcc and LLVM guides on adding new targets:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://gcc.gnu.org/onlinedocs/gccint/Back-End.html&quot; target=&quot;_blank&quot;&gt;gcc - Anatomy of a Target Back End&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://llvm.org/docs/WritingAnLLVMBackend.html&quot; target=&quot;_blank&quot;&gt;Writing an LLVM Backend&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The cozy machine spec is a cross between ini files, yaml, and a custom syntax. The format is somewhat complex, but once understood is intuitive and flexible. At the top level, it looks like an ini file:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;registers&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;macros&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;instructions&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Metadata&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;metadata&lt;/strong&gt; section contains some high-level information about the architecture design, and is the simplest section to understand. It currently looks like this for z80:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;string property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;z80&lt;/span&gt;
&lt;span class=&quot;string property&quot;&gt;bits&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;
&lt;span class=&quot;string property&quot;&gt;endianness&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;little&lt;/span&gt;
&lt;span class=&quot;string property&quot;&gt;signedness&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;twos-complement&lt;/span&gt;
&lt;span class=&quot;string property&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;none&lt;/span&gt;
&lt;span class=&quot;string property&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This isn’t comprehensive, and I’ll be adding more metadata as it becomes necessary. On LLVM, this sort of information is encoded into a string that looks something like this: &lt;code&gt;&amp;quot;e-p:16:8:8-i8:8:8-i16:8:8-n8:16&amp;quot;&lt;/code&gt;. This string is passed into the &lt;code&gt;LLVMTargetMachine&lt;/code&gt; base constructor in C++. I think we can do a hell of a lot better than that!&lt;/p&gt;&lt;h3&gt;Registers&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;registers&lt;/strong&gt; section describes the registers on this architecture.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;registers&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
BC: 16
    B: 8
    C: 8; offset=8
DE: 16
    D: 8
    E: 8; offset=8
HL: 16
    H: 8
    L: 8; offset=8
SP: 16; stack
PC: 16; program
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we can start to see some interesting syntax and get an idea of the design of cozy machine specs. The contents of each section are keys, which have values, attributes, and children. The format looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;string property&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;value; attributes, ...
    children...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we’ve defined the BC, DE, HL, SP, and PC registers. HL, DE, and BC are general purpose 16-bit registers, and each can also be used as two separate 8-bit registers. The attributes for these sub-registers indicates their offsets in the parent register. We also define the stack and program registers, SP and PC, which use the stack and program attributes to indicate their special purposes.&lt;/p&gt;&lt;p&gt;We can also describe CPU flags in this section:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;registers&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
AF: 16; special
    A: 8; accumulator
    F: 8; flags, offset 8;; flag
        _C:  1
        _N:  1; offset 1
        _PV: 1; offset 2
        _3:  1; offset 3, undocumented
        _H:  1; offset 4
        _5:  1; offset 5, undocumented
        _Z:  1; offset 6
        _S:  1; offset 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we introduce another feature of cozy specs with &lt;code&gt;F: 8; flags, offset 8;; flag&lt;/code&gt;. Using &lt;code&gt;;;&lt;/code&gt; adds those attributes to all children of this key, so each of _C, _N, etc have the &lt;code&gt;flag&lt;/code&gt; attribute.&lt;/p&gt;&lt;p&gt;Take note of the “undocumented” attribute here. Some of the metadata included in a spec can be applied to cozy tools. Some of it, however, is there for other tools to utilize. We have a good opportunity to make a machine-readable description of the architecture, so I’ve opted to include a lot of extra details in machine specs that third parties could utilize (though there might be a -fno-undocumented compiler flag some day, I guess).&lt;/p&gt;&lt;h3&gt;Macros&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;macros&lt;/strong&gt; section is heavily tied to the instructions section. Most instruction sets are quite large, and I don’t want to burden spec authors with writing out the entire thing. We can speed up their work by providing macros.&lt;/p&gt;&lt;p&gt;z80 instructions have a few sets of common patterns in their encodings. Register groups are often represented by the same set of bits, and we can make our instruction set specification more concise by taking advantage of this. For example, here’s a macro that we can use for instructions that can use either the BC, DE, HL, or SP registers:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;macros&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;string property&quot;&gt;reg_BCDEHLSP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;string property&quot;&gt;BC&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;00&lt;/span&gt;
    &lt;span class=&quot;string property&quot;&gt;DE&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;01&lt;/span&gt;
    &lt;span class=&quot;string property&quot;&gt;HL&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;
    &lt;span class=&quot;string property&quot;&gt;SP&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have the name of the macro as the top-level key, in this case &lt;code&gt;reg_BCDEHLSP&lt;/code&gt;. We can later refer to this macro with &lt;code&gt;@reg_BCDEHLSP&lt;/code&gt;. Then, we have each of the cases it can match on, and the binary values these correspond to when encoded in an instruction.&lt;/p&gt;&lt;h3&gt;Instructions&lt;/h3&gt;&lt;p&gt;The instructions section brings everything together and defines the actual instructions available on this architecture. Instructions can be organized into groups at the spec author’s pleasure, which can be referenced by derivative architectures. Here we can take a look at the “load” group:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;instructions&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;
.load:
    ld:
        @reg_BCDEHLSP, @imm[16]: 00 $1 0001 $2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On z80, the &lt;code&gt;ld&lt;/code&gt; instruction is similar to the &lt;code&gt;mov&lt;/code&gt; instruction on Intel architectures. It assigns the second argument to the first. This could be used to assign registers to each other (e.g. &lt;code&gt;ld a, b&lt;/code&gt; to set A = B), to set registers to constants, and so on. Our example here uses our macro from earlier to match instructions like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ld hl, 0x1234
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The value for this key may reference the arguments with variables. $1 here equals &lt;code&gt;10&lt;/code&gt;, from the macro. The &lt;code&gt;imm&lt;/code&gt; built-in is implemented in C to match constants and provides $2. An assembler could use this information to assemble our example instruction into this machine code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;00100001 00110100 00010010
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which will load HL with the value 0x1234 when executed.&lt;/p&gt;&lt;h3&gt;Lots more metadata&lt;/h3&gt;&lt;p&gt;Now that we have the basics down, let’s dive into some deeper details. Cozy specs are designed to provide most of the information the &lt;em&gt;entire toolchain&lt;/em&gt; needs to support an architecture. The information we have so far could be used to generate assemblers and disassemblers, but I want this file to be able to generate things like optimizers as well. You can add the necessary metadata to each instruction by utilizing attributes.&lt;/p&gt;&lt;p&gt;Consider the z80 instruction LDIR, which stands for “load/decrement/increment/repeat”. This instruction is used for memcpy operations. To use it, you set the HL register to a source address, the DE register to a destination address, and BC to a length. This instruction looks like this in the spec:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;string property&quot;&gt;ldir&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;11101101 10110000; uses[HL, DE, BC], \
    affects[HL[+BC], DE[+BC], BC[0]], \
    flags[_H:0,_N:0,_PV:0], cycles[16 + BC * 5]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s a lot of attributes! The purpose of these attributes are to give the toolchain insights into the registers this instruction uses, its side effects, and how fast it is. These attributes can help us compare the efficiency of different approaches and understand the how the state of registers evolves during a function, which leads to all sorts of useful optimizations.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;affects&lt;/code&gt; attribute, for example, tells us how each register is affected by this instruction. We can see that after this instruction, HL and DE will have had BC added to them, and BC will have been set to 0. We can make all sorts of optimizations based on this knowledge. Here are some examples:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dest&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;memcpy&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;dest&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;len&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler can assign &lt;code&gt;src&lt;/code&gt; to HL, &lt;code&gt;dest&lt;/code&gt; to DE, and &lt;code&gt;len&lt;/code&gt; to BC. We can then optimize out the final statement entirely because we know that the LDIR instruction will have already added BC to HL for us.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;dest&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;memcpy&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;dest&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;src&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;len&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;foobar&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, the register allocator can just assign BC to &lt;code&gt;foobar&lt;/code&gt; and avoid initializing it because we know it’s already going to be zero. Many other optimizations are made possible when we are keeping track of the side effects of each instruction.&lt;/p&gt;&lt;h2&gt;Next steps&lt;/h2&gt;&lt;p&gt;I’ve iterated over this spec design for a while now, and I’m pretty happy with it. I would love to hear your feedback. Assuming that this looks good, my next step is writing more specs, and a tool that parses and compiles them to C. These C files are going to be linked into &lt;code&gt;libcozyspec&lt;/code&gt;, which will provide an API to access all of this metadata from C. It will also include an instruction matcher, which will be utilized by the next step - writing the assembler.&lt;/p&gt;&lt;p&gt;The assembler is going to take a while, because I don’t want to go the gas route of making a half-baked assembler that’s more useful for compiling the C compiler’s output than for anything else. I want to make an assembler that assembly programmers would &lt;em&gt;want&lt;/em&gt; to use.&lt;/p&gt;&lt;p&gt;I have not yet designed an intermediate bytecode for the compiler to use, but one will have to be made. The machine spec will likely change somewhat to accommodate this. Some of the conversion from internal bytecode to target assembly can likely be inferred from metadata, but some will have to be done manually for each architecture.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://sr.ht/7_Pe.txt&quot; target=&quot;_blank&quot;&gt;Here’s the entire z80 spec&lt;/a&gt; I’ve been working on, for your reading pleasure.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/cozy-devnotes-machine-specs/</link>
        
        <pubDate>Wed, 22 Feb 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/cozy-devnotes-machine-specs/</guid>
      </item>
    
      <item>
        
        
          <title>Lessons to learn from C</title>
          <description>
            &lt;p&gt;C is my favorite language, though I acknowledge that it has its warts. I’ve tried looking at languages people hope will replace C (Rust, Go, etc), and though they’ve improved on some things they won’t be supplanting C in my life any time soon. I’ll share with you what makes C a great language to me. Take some of these things as inspiration for the next C replacement you write.&lt;/p&gt;&lt;p&gt;First of all, it’s important to note that I’m talking about the language, not its standard library. The C standard library isn’t &lt;em&gt;awful&lt;/em&gt;, but it certainly leaves a lot to be desired. I also want to place a few limitations on the kind of C we’re talking about - you can write bad code in any language, and C is no different. For the purpose of argument, let’s assume the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;C99 minimum&lt;/li&gt;&lt;li&gt;Absolutely no code in headers - just type definitions and function prototypes&lt;/li&gt;&lt;li&gt;Minimal use of typedefs&lt;/li&gt;&lt;li&gt;No macros&lt;/li&gt;&lt;li&gt;No compiler extensions&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I hold myself to these guidelines when writing C, and it is from this basis that I compare other languages with C. It’s not useful to compare bad C to another language, because I wouldn’t want to write bad C either.&lt;/p&gt;&lt;p&gt;Much of what I like about C boils down to this: &lt;strong&gt;C is simple&lt;/strong&gt;. The ultimate goal of any system should be to attain the simplest solution for the problems it faces. C prefers to be conservative with new features. The lifetime of a feature in Rust, for example, from proposal to shipping is generally 0 to 6 months. The same process in C can take up to 10 years. C is a venerable language, and has already long since finished adding core features. It is stable, simple, and reliable.&lt;/p&gt;&lt;p&gt;To this end, language features map closely to behaviors common to most CPUs. C strikes a nearly perfect balance of usability versus simplicity, which results in a small set of features that are easy to reason about. A C expert could roughly predict the assembly code produced by their compiler (assuming &lt;code&gt;-O0&lt;/code&gt;) for any given C function. It follows that C compilers are easy to write and reason about.&lt;/p&gt;&lt;p&gt;The same person would also be able to give you a rough idea of the performance characteristics of that function, pointing out things like cache misses and memory accesses that are draining on speed, or giving you a precise understanding of how the function handles memory. If I look at a function in other languages, it’s much more difficult to discern these things with any degree of precision without actually compiling the code and looking at the output.&lt;/p&gt;&lt;p&gt;The compiler also integrates very comfortably with the other tools near it, like the assembler and linker. Symbols in C map 1:1 to symbols in the object files, which means linking objects together is simple and easily reasoned about. It also makes interop with other languages and tools straightforward - there’s a reason every language has a means of writing C bindings, but not generally C++ bindings. The use of headers to declare external symbols and types is also nicer than some would have you believe, since it gives you an opportunity to organize and document your API.&lt;/p&gt;&lt;p&gt;C is also the most portable programming language in the world. Every operating system on every architecture has a C compiler, and they weren’t really considered a viable platform until it did. Once you have a C compiler you generally have everything else, because everything else was either written in C or was written in a language that was implemented in C. I can write C programs on/for Linux, Windows, BSD, Minix, plan9, and a dozen other niche operating systems, or even no operating system, on pretty much any CPU architecture I want. No other language supports nearly as many platforms as C does.&lt;/p&gt;&lt;p&gt;With these benefits acknowledged, there are some things C could do better. The standard library is one of them, but we can talk about that some other time. Another is generics; using void* all the time isn’t good. Some features from other languages would be nice - I would take something similar to Rust’s match keyword. Of course, the fragility of memory management in C is a concern that other languages are wise to address. Undefined behavior is awful.&lt;/p&gt;&lt;p&gt;Even for all of these warts, however, the basic simplicity and elegance of C keeps me there. I would love to see a language that fixes these problems without trying to be the kitchen sink, too.&lt;/p&gt;&lt;p&gt;In short, I like C because &lt;strong&gt;C is simple&lt;/strong&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Lessons-to-learn-from-C/</link>
        
        <pubDate>Mon, 30 Jan 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Lessons-to-learn-from-C/</guid>
      </item>
    
      <item>
        
        
          <title>The only problem with Python 3&apos;s str is that you don&apos;t grok it</title>
          <description>
            &lt;p&gt;I’ve found myself explaining Python 3’s str to people online more and more often lately. There’s this ridiculous claim about that Python 3’s string handling is broken or somehow worse than Python 2, and today I intend to put that myth to rest.  Python 2 strings are broken, and Python 3 strings are sane. The only problem is that you don’t grok strings.&lt;/p&gt;&lt;p&gt;The basic problem many people seem to have with Python 3’s strings arises when they write code that treats bytes like a string, because that’s how it was in Python 2. Let me make this as clear as possible:&lt;/p&gt;&lt;div class=&quot;loud&quot;&gt;a bytes is not a string&lt;/div&gt;

&lt;style&gt;
.loud {
    font-size: 14pt;
    font-weight: bold;
    text-align: center;
    margin-bottom: 1rem;
}
&lt;/style&gt;
&lt;p&gt;I want you to read that, over and over again, until it sinks in. A string is basically an array of characters (characters being Unicode codepoints), whereas bytes is an array of bytes, aka octets, aka unsigned 8 bit integers. That’s right - bytes is an array of unsigned 8 bit integers, or as the name would imply, bytes.  If you &lt;em&gt;ever&lt;/em&gt; do string operations against bytes, you are Doing It Wrong because bytes are not strings.&lt;/p&gt;&lt;div class=&quot;loud&quot;&gt;a bytes is not a string&lt;/div&gt;
&lt;p&gt;It’s entirely possible that your bytes contains an &lt;em&gt;encoded representation&lt;/em&gt; of a string. That encoding could be ASCII, UTF-8, UTF-32, etc. These encodings are means of representing strings as bytes, aka unsigned 8 bit integers. In order to treat it like a string, you first must &lt;em&gt;decode&lt;/em&gt; it. Luckily Python 3 makes this painless: &lt;code&gt;bytes.decode()&lt;/code&gt;. This defaults to UTF-8, but you can specify any encoding you want: &lt;code&gt;bytes.decode(&amp;apos;latin-1&amp;apos;)&lt;/code&gt;. If you want bytes again, use &lt;code&gt;str.encode()&lt;/code&gt;, which again defaults to UTF-8 but accepts any encoding. If you have a bytes that contains an encoded string, your first order of business is decoding it.&lt;/p&gt;&lt;div class=&quot;loud&quot;&gt;a bytes is not a string&lt;/div&gt;
&lt;p&gt;Let’s look at some examples of why this matters in practice:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;Python&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;3.6&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;.0&lt;/span&gt; (&lt;span class=&quot;constructor constant variable&quot;&gt;default&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;Dec&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;24&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2016&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;08&lt;/span&gt;:&lt;span class=&quot;number&quot;&gt;03&lt;/span&gt;:&lt;span class=&quot;number&quot;&gt;08&lt;/span&gt;) 
[&lt;span class=&quot;constructor constant variable&quot;&gt;GCC&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;6.2&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;.1&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;20160830&lt;/span&gt;] &lt;span class=&quot;constructor constant variable&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;linux&lt;/span&gt;
&lt;span class=&quot;constructor constant variable&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;help&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;copyright&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;credits&amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;license&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;more&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;information&lt;/span&gt;.
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;[::&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;]
&lt;span class=&quot;string&quot;&gt;&amp;apos;すまいざごうよはお&amp;apos;&lt;/span&gt;
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]
&lt;span class=&quot;string&quot;&gt;&amp;apos;お&amp;apos;&lt;/span&gt;
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;]
&lt;span class=&quot;string&quot;&gt;&amp;apos;は&amp;apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or in Python 2:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;Python&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;.13&lt;/span&gt; (&lt;span class=&quot;constructor constant variable&quot;&gt;default&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;Dec&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;21&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2016&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;07&lt;/span&gt;:&lt;span class=&quot;number&quot;&gt;16&lt;/span&gt;:&lt;span class=&quot;number&quot;&gt;46&lt;/span&gt;) 
[&lt;span class=&quot;constructor constant variable&quot;&gt;GCC&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;6.2&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;.1&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;20160830&lt;/span&gt;] &lt;span class=&quot;constructor constant variable&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;linux2&lt;/span&gt;
&lt;span class=&quot;constructor constant variable&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;help&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;copyright&amp;quot;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;credits&amp;quot;&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;license&amp;quot;&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;more&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;information&lt;/span&gt;.
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x8a&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xaf&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x82&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x88&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x86&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x94&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x96&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x84&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xbe&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x99&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;[::&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;]
&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x99&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xbe&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x84&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x96&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x94&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x86&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x88&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x82&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xaf&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x8a&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;[::&lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;])
&lt;span class=&quot;constructor constant variable&quot;&gt;㾁&lt;/span&gt;㄁&lt;span class=&quot;constructor constant variable&quot;&gt;㖁㔁ㆁ&lt;/span&gt;㈂&lt;span class=&quot;constructor constant variable&quot;&gt;㯁&lt;/span&gt;㊁&lt;span class=&quot;constructor constant variable&quot;&gt;ã&lt;/span&gt;
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;]
&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xe3&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;
&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;おはようございます&amp;apos;&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;]
&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x81&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For anything other than ASCII, Python 2 “strings” are broken. Python 3’s string handling is superb. The problem with it has only ever been that you don’t actually know how strings work. Instead of starting ignorant flamewars about it, learn how it works.&lt;/p&gt;&lt;h2&gt;Actual examples people have given me&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;“Python 3 can’t handle bytes as file names”&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Yes it can. Just stop treating them like strings:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;open&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;b&amp;apos;test-&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\xd8&lt;/span&gt;&lt;span class=&quot;string escape&quot;&gt;\x01&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;.txt&amp;apos;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;apos;w&amp;apos;&lt;/span&gt;).&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;close&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the use of bytes as the file name, not str. \xd8\x01 is unrepresentable as UTF-8.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; [&lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;open&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;f&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;apos;r&amp;apos;&lt;/span&gt;).&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;close&lt;/span&gt;() &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;os&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;listdir&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;b&amp;apos;.&amp;apos;&lt;/span&gt;)]
[&lt;span class=&quot;constant_builtin&quot;&gt;None&lt;/span&gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the use of bytes as the path to os.listdir (the documentation says that if you want bytes back as file names, pass bytes as the path. The docs are helpful like that). Also note the lack of crashes or broken behavior.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;“Python 3’s csv module writes b’Hello’,b’World’ into CSV files”&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;CSV files are “comma seperated values”. Is each value an array of unsigned 8 bit integers? No, of course not. They’re strings. So why would you pass an array of unsigned 8 bit integers to it?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;“Python 3 doesn’t support writing files as latin-1”&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Sure it does.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;open&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;apos;some latin-1 file&amp;apos;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;apos;rb&amp;apos;&lt;/span&gt;) &lt;span class=&quot;keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;f&lt;/span&gt;:
  &lt;span class=&quot;constructor constant variable&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;f&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;read&lt;/span&gt;().&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;decode&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;apos;latin-1&amp;apos;&lt;/span&gt;)
&lt;span class=&quot;keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;open&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;apos;some utf8 file&amp;apos;&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;apos;wb&amp;apos;&lt;/span&gt;) &lt;span class=&quot;keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;f&lt;/span&gt;:
  &lt;span class=&quot;constructor constant variable&quot;&gt;f&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;write&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;text&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;encode&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;apos;utf-8&amp;apos;&lt;/span&gt;))
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;loud&quot;&gt;a bytes is not a string&lt;/div&gt;

&lt;div class=&quot;loud&quot;&gt;a bytes is not a string&lt;/div&gt;

&lt;div class=&quot;loud&quot;&gt;a bytes is not a string&lt;/div&gt;
&lt;p&gt;Python 2’s shitty design has broken your mindset. Unlearn it.&lt;/p&gt;&lt;h2&gt;Python 2 is dead, long live Python 3&lt;/h2&gt;&lt;p&gt;Listen. It’s time you moved to Python 3. You’re missing out on a lot of really great improvements to the language and are stuck with a lot of problems. Python 2 is really being EoL’d, and closing your eyes and covering your ears singing “la la la” doesn’t change that. The transition is really not that difficult or time consuming, and well worth it. Some people say only new projects should be written in Python 3. I say that’s bollocks - all projects should be written in Python 3 and you need to migrate, &lt;em&gt;now&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Python 3 is better. Much, much better. For every legitimate criticism of Python 3 I’ve seen, I’ve seen 10 that are bullshit. Come join us in the wonderful world of sane string handling, type decorations, async/await, and more awesome features. Every library supports it now. Let go of your biases and evaluate the language honestly.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-problem-with-Python-3/</link>
        
        <pubDate>Fri, 13 Jan 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-problem-with-Python-3/</guid>
      </item>
    
      <item>
        
        
          <title>Actually, you CAN do it</title>
          <description>
            &lt;p&gt;I maintain a &lt;em&gt;lot&lt;/em&gt; of open source projects. In order to do so, I have to effectively manage my time. Most of my projects follow this philosophy: if you want something changed, send a patch. If you are running into an annoying bug, fix it and send a patch. If you want a new feature, implement it and send a patch. It’s definitely a good idea to talk about it beforehand on the issue tracker or IRC, but don’t make the mistake of thinking this processes ends with someone else doing it for you.&lt;/p&gt;&lt;p&gt;Every developer who contributes to a project I maintain is self-directed. They work on what they’d like. They scratch their own itches. Sometimes what they’d like to work on is non-specific, and in that case I’ll help them find something to do based on what users are asking for lately or based on my own goals for the project. I often maintain a list of “low hanging fruit” issues on Github, and I am generally willing to offer some suggestions if someone asks for such a task. However, for more complex, non-“low hanging fruit” tasks, they generally only get worked on when someone with the know-how wants it done and does it.&lt;/p&gt;&lt;p&gt;So what does this mean for you, user whose problem no developer is interested in? Well, it’s time for you to step up and work on it yourself. I don’t really care if your problem is “a showstopper” or “the only thing preventing you from switching to my software”, or any of a number of other excuses you may have lined up for getting someone else to do it for you. None of the other regular contributors really care about your interpretation of what their priorities should be, either. We aren’t a business. We aren’t making a sale. We’re just making cool software that works for us and publishing it in the hopes that you’ll find it useful, too.&lt;/p&gt;&lt;p&gt;Generally by this point in the conversation with Joe User, they tell me they &lt;em&gt;can’t&lt;/em&gt; do it. Well, Joe User, I beg to differ. It doesn’t matter that you don’t know &lt;em&gt;[insert programming language]&lt;/em&gt;, or haven’t used &lt;em&gt;[insert relevant library]&lt;/em&gt; before. You don’t learn new things by hanging out in your comfort zone. Many of the regulars you’re bugging to do your work for you were once in your shoes.&lt;/p&gt;&lt;p&gt;Everything is setting you up for success. You literally have hundreds of resources at your disposal. The internet is was made by developers, you know, and we built tons of resources to support ourselves with it. You have documentation, Q&amp;A sites, chat rooms, and more waiting to help you when you get stuck. We’re here to answer your questions with the codebase, too. I pride myself on making the code accessible and easy to get into, and I’ll help you learn to do the same when you integrate your with our project.&lt;/p&gt;&lt;p&gt;We would much rather give you advice on how to fix the problem yourself than to fix the problem for you. Even if it takes more of our attention to do so, we get the added benefit of a new person who is qualified to help out the next guy. A person who is now fixing their own bugs and improving the software for everyone. That’s a much better outcome than having to waste our own time on a task we aren’t interested in.&lt;/p&gt;&lt;p&gt;It might be hard, but hey, it’d be hard for us too. You’ll learn and be better for it. Wouldn’t it be nice to add &lt;em&gt;[language you don’t know]&lt;/em&gt; or &lt;em&gt;[library you don’t know]&lt;/em&gt; to your resume, anyway? If you’re concerned about the scope of your problem, how about asking about the low hanging fruit so you have easier tasks to learn with?&lt;/p&gt;&lt;p&gt;The cards are stacked in your favor. The only problem is your defeatist attitude. Just do it!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Actually-you-CAN-do-it/</link>
        
        <pubDate>Fri, 06 Jan 2017 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Actually-you-CAN-do-it/</guid>
      </item>
    
      <item>
        
        
          <title>State of Sway December 2016 - secure your Wayland desktop, get paid to work on Sway</title>
          <description>
            &lt;p&gt;Earlier today I released &lt;a href=&quot;https://github.com/SirCmpwn/sway/releases/tag/0.11&quot; target=&quot;_blank&quot;&gt;sway 0.11&lt;/a&gt;, which (along with lots of the usual new features and bug fixes) introduces support for security policies that can help realize the promise of a secure Wayland desktop. We also just started a bounty program that lets you sponsor the things you want done and rewards contributors for working on them.&lt;/p&gt;&lt;p&gt;Today sway has 19,371 lines of C (and 3,761 lines of header files) written by 70 authors across 2,067 commits. These were written through 589 pull requests and 425 issues. Sway packages are available today in the official repos of Arch, Gentoo, Fedora, NixOS, openSUSE, Void Linux, and more. Sway looks like this:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/ICd5.png&quot;&gt;&lt;/p&gt;&lt;p&gt;Side note: please add pretty screenshots of sway to &lt;a href=&quot;https://github.com/SirCmpwn/sway/wiki/Screenshots-of-Sway&quot; target=&quot;_blank&quot;&gt;this wiki page&lt;/a&gt;. Thanks!&lt;/p&gt;&lt;p&gt;For those who are new to the project, &lt;a href=&quot;http://swaywm.org&quot; target=&quot;_blank&quot;&gt;Sway&lt;/a&gt; is an i3-compatible Wayland compositor. That is, your existing &lt;a href=&quot;http://i3wm.org/&quot; target=&quot;_blank&quot;&gt;i3&lt;/a&gt; configuration file will work as-is on Sway, and your keybindings and colors and fonts and for_window rules and so on will all be the same. It’s i3, but for Wayland, plus it’s got some bonus features. Here’s a quick rundown of what’s new since the &lt;a href=&quot;https://drewdevault.com/blog/Sway-0.9-in-retro/&quot;&gt;previous state of Sway&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Security policy configuration (man sway-security)&lt;/li&gt;&lt;li&gt;FreeBSD support&lt;/li&gt;&lt;li&gt;Initial support for HiDPI among sway clients (swaybar et al)&lt;/li&gt;&lt;li&gt;Support for new i3 features&lt;/li&gt;&lt;li&gt;Clicky title bars&lt;/li&gt;&lt;li&gt;Lots of i3 compatability improvements&lt;/li&gt;&lt;li&gt;Lots of documentation improvements&lt;/li&gt;&lt;li&gt;Lots of bugfixes&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Today it seems that most of the features sway needs are implemented. Work hasn’t slowed down - there’s been lots of work fixing small bugs, improving documentation, fixing subtle incompatabilities with i3, and so on. However, to encourage the development of new features, I’ve officially put into action the new bounty program today. Here’s how it works - you can donate to the features you want to see, and you can claim the donations by implementing the features and sending a pull request. To date I’ve received about $200 in donations towards sway, and I’ve matched that with a donation of my own to bring it up to $400. I’ve distributed these donations into various buckets of features. Not every feature is for sway - anything that improves the sway experience is eligible for a bounty, and in fact over half of the initial bounties are for features in other parts of the ecosystem. For details on the program, check out &lt;a href=&quot;https://github.com/SirCmpwn/sway/issues/986&quot; target=&quot;_blank&quot;&gt;this link&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Here’s the updated stats. First, &lt;strong&gt;lines of code per author&lt;/strong&gt;:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;3799 (+775)&lt;/td&gt;&lt;td&gt;Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;3489 (-1170)&lt;/td&gt;&lt;td&gt;Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;1705 (-527)&lt;/td&gt;&lt;td&gt;taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;1236 (-550)&lt;/td&gt;&lt;td&gt;S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;1160 (+70)&lt;/td&gt;&lt;td&gt;Zandr Martin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;449 (-12)&lt;/td&gt;&lt;td&gt;minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;311 (-54)&lt;/td&gt;&lt;td&gt;Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;285 (+285)&lt;/td&gt;&lt;td&gt;D.B&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;247 (-87)&lt;/td&gt;&lt;td&gt;Kevin Hamacher&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;227 (-298)&lt;/td&gt;&lt;td&gt;Cole Mickens&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;219 (+219)&lt;/td&gt;&lt;td&gt;David Eklov&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Finally, I’m the top contributor! I haven’t been on top for over a year. Lots of the top contributors are slowly having their lines of code reduced as lots of new contributors are coming in and displacing them with refactorings and bug fixes.&lt;/p&gt;&lt;p&gt;Here’s the total &lt;strong&gt;number of commits per author&lt;/strong&gt; for each of the top ten committers:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;1009&lt;/td&gt;&lt;td&gt; Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;245&lt;/td&gt;&lt;td&gt; Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;153&lt;/td&gt;&lt;td&gt; taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;97&lt;/td&gt;&lt;td&gt; Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;91&lt;/td&gt;&lt;td&gt; S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;68&lt;/td&gt;&lt;td&gt; Zandr Martin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;58&lt;/td&gt;&lt;td&gt; Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;45&lt;/td&gt;&lt;td&gt; D.B&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;33&lt;/td&gt;&lt;td&gt; Taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt; minus&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most of what I do for Sway personally is reviewing and merging pull requests. Here’s the same figures using &lt;strong&gt;number of commits per author, excluding merge commits&lt;/strong&gt;, which changes my stats considerably:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;479&lt;/td&gt;&lt;td&gt; Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;229&lt;/td&gt;&lt;td&gt; Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;138&lt;/td&gt;&lt;td&gt; taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;96&lt;/td&gt;&lt;td&gt; Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;91&lt;/td&gt;&lt;td&gt; S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;58&lt;/td&gt;&lt;td&gt; Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt; Zandr Martin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;45&lt;/td&gt;&lt;td&gt; D.B&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt; Taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt; minus&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These stats only cover the top ten in each, but there are more - check out the &lt;a href=&quot;https://github.com/SirCmpwn/sway/graphs/contributors&quot; target=&quot;_blank&quot;&gt;full list&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Here’s looking forward to sway 1.0 in 2017!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/State-of-sway/</link>
        
        <pubDate>Tue, 27 Dec 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/State-of-sway/</guid>
      </item>
    
      <item>
        
        
          <title>A broad intro to networking</title>
          <description>
            &lt;p&gt;Disclaimer: I am not a network engineer. That’s the point of this blog post, though - I want to share with non-networking people enough information about networking to get by. Hopefully by the end of this post you’ll know enough about networking to keep up with a conversation on networking, or know what to search for when something breaks, or know what tech to research more in-depth when you are putting together something new.&lt;/p&gt;&lt;h2&gt;Layers&lt;/h2&gt;&lt;p&gt;The &lt;strong&gt;OSI model&lt;/strong&gt; is the standard model we describe networks with. There are 7 &lt;strong&gt;layers&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;Layer 1, the physical layer, is the electrical engineering stuff.&lt;/p&gt;&lt;p&gt;Layer 2, the link layer, is how devices talk to each other.&lt;/p&gt;&lt;p&gt;Layer 3, the network layer, is what they talk about.&lt;/p&gt;&lt;p&gt;Layer 4, the transport layer, is where things like TCP and UDP live.&lt;/p&gt;&lt;p&gt;Layers 5 and 6 aren’t very important.&lt;/p&gt;&lt;p&gt;Layer 7, the application layer, is where Minecraft lives.&lt;/p&gt;&lt;p&gt;When you hear some security guy talking about a “layer 7 attack”, he’s talking about a attack that focuses on flaws in the application layer. In practice that means i.e. flooding the server with HTTP requests.&lt;/p&gt;&lt;h2&gt;1: Physical Layer&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Generally implemented by matter&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Layer 1 is the hardware of a network. Commonly you’ll find things here like your computer’s &lt;strong&gt;NIC&lt;/strong&gt; (network interface controller), aka the network interface or just the interface, which is the bit of silicon in your PC that you plug network cables or WiFi signals into.&lt;/p&gt;&lt;p&gt;On Linux, network interfaces are assigned names like &lt;em&gt;eth0&lt;/em&gt; or &lt;em&gt;eno1&lt;/em&gt;. eth0 is the traditional name for the 0th wired network interface. eno1 is the newer “consistent network device naming” format popularized by tools like udev (which manages hardware on many Linux systems) - this is a deterministic name based on your network hardware, and won’t change if you add more interfaces. You can manage your interfaces with the &lt;em&gt;ip&lt;/em&gt; command (&lt;code&gt;man 8 ip&lt;/code&gt;), or the now-deprecated &lt;em&gt;ifconfig&lt;/em&gt; command. Some non-Linux Unix systems have not deprecated ifconfig.&lt;/p&gt;&lt;p&gt;This layer also has ownership over &lt;strong&gt;MAC addresses&lt;/strong&gt;, in theory. A MAC address is an allegedly unique identifier for a network device. In practice, software at higher layers can use whatever MAC address they want. You can change your MAC address with the ip command, which is often useful for dealing with annoying public WiFi resource limits or for frustrating someone else on the network.&lt;/p&gt;&lt;p&gt;Other things you find at layer 1 include &lt;strong&gt;switches&lt;/strong&gt;, which do network multiplexing (they generally can be thought of as networking’s version of a power strip - they turn one Ethernet port into many). Also common are &lt;strong&gt;routers&lt;/strong&gt;, whose behaviors are better explained in other layers. You also have hardware like &lt;strong&gt;firewalls&lt;/strong&gt;, which filter network traffic, and &lt;strong&gt;load balancers&lt;/strong&gt;, which distribute a load among several nodes. Both firewalls and load balancers can be done in software, depending on your needs.&lt;/p&gt;&lt;h2&gt;2: Data link layer&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Generally implemented by network hardware&lt;/em&gt;&lt;/p&gt;&lt;p&gt;At this layer you have protocols that cover how nodes talk to one another. Here the &lt;strong&gt;ethernet&lt;/strong&gt; protocol is almost certainly the most common - the protocol that goes over your network cables. Said network cables are probably &lt;strong&gt;Cat 5&lt;/strong&gt; cables, or “category 5” cables.&lt;/p&gt;&lt;p&gt;Other protocols here include tunnels, which allow you to indirectly access a network. A common example is a &lt;strong&gt;VPN&lt;/strong&gt;, or virtual private network, which allows you to participate in another network remotely. Tunnels can also be useful for getting around firewalls, or for setting up a secure means to access resources on another network.&lt;/p&gt;&lt;h2&gt;3: Network layer&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Generally implemented by the kernel&lt;/em&gt;&lt;/p&gt;&lt;p&gt;As a software guy, this is where the fun really starts. The other layers are how computers talk to each other - this layer is what they talk about. Computers are often connected via a &lt;strong&gt;LAN&lt;/strong&gt;, or local area network - a &lt;em&gt;local&lt;/em&gt; network of computers. Computers are also often connected to a &lt;strong&gt;WAN&lt;/strong&gt;, or wide area network - the internet is one such network.&lt;/p&gt;&lt;p&gt;The most common protocol at this layer is IP, or Internet Protocol. There are two versions that matter: IPv4, and IPv6. Both of them use &lt;strong&gt;IP addresses&lt;/strong&gt; to identify nodes on their networks, and they carry &lt;strong&gt;packets&lt;/strong&gt; between them. The major difference between IPv4 and IPv6 is the size of their respective &lt;strong&gt;address spaces&lt;/strong&gt;. IPv4 uses 32 bit addresses, supporting a total of 4.3 billion possible addresses, which on the public internet are quickly becoming a sparse resource. IPv6 uses 128-bit addresses, which allows for a zillion unique addresses.&lt;/p&gt;&lt;p&gt;Ranges of IP addresses can be described with a &lt;strong&gt;subnet mask&lt;/strong&gt;. Such a range of IP addresses constitutes a &lt;strong&gt;subnetwork&lt;/strong&gt;, or subnet. Though you’re probably used to seeing an IPv4 address encoded like &lt;code&gt;10.20.30.40&lt;/code&gt;, remember that it can also just be represented as one 32-bit number - in this case 169090600, or 0xA141E28, and you can do bitwise math against these numbers. You generally represent a subnet with CIDR notation, such as &lt;code&gt;192.168.1.0/24&lt;/code&gt;. In this case, the first 24 bits are meaningful, and all possible values for the remaining 8 bits constitute the range of addresses represented by this mask.&lt;/p&gt;&lt;p&gt;IPv4 has several subnets reserved for this and that. Some important ones are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;0.0.0.0/8&lt;/code&gt; - current network. On many systems, you can treat &lt;code&gt;0.0.0.0&lt;/code&gt; as all IP addresses assigned to your device&lt;/li&gt;&lt;li&gt;&lt;code&gt;127.0.0.0/8&lt;/code&gt; - loopback network. These addresses refer to yourself.&lt;/li&gt;&lt;li&gt;&lt;code&gt;10.0.0.0/8&lt;/code&gt;, &lt;code&gt;172.16.0.0/12&lt;/code&gt;, and &lt;code&gt;192.168.0.0/16&lt;/code&gt; are reserved for private networks - you can allocate these addresses on a LAN.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;An IPv4 packet includes, among other things: a &lt;strong&gt;time to live&lt;/strong&gt;, or TTL, which limits how long the packet can live for; the &lt;strong&gt;protocol&lt;/strong&gt;, such as TCP; the &lt;strong&gt;source&lt;/strong&gt; and &lt;strong&gt;destination&lt;/strong&gt; addresses; a header checksum; and the &lt;strong&gt;payload&lt;/strong&gt;, which is specific to the higher level protocol in use.&lt;/p&gt;&lt;p&gt;Given the limited size of the IPv4 space, most networks are designed with an isolated LAN that uses &lt;strong&gt;NAT&lt;/strong&gt;, or network address translation, to translate IP addresses from the WAN. Basically, a router or similar component will translate internal IP addresses (allocated from the private subnets) to its own external IP address, and vice versa, when passing communications along to the WAN. With IPv6 there are so many IP addresses that you don’t need to use NAT. If you’re wondering whether or not we’ll ever run out of IPv6 addresses - leave that to someone else to solve tens of millions of years from now.&lt;/p&gt;&lt;p&gt;IPv6 addresses are 128-bits long and are described with strings like &lt;code&gt;2001:0db8:0000:0000:0000:ff00:0042:8329&lt;/code&gt;. Luckily the people who designed it were kind enough to realize people don’t want to write that, so it can be shortened to &lt;code&gt;2001:db8::ff00:42:8329&lt;/code&gt; by removing leading zeros and removing sections entirely composed of zeros. Where colons are reserved for another purpose, you’ll typically add brackets around the IPv6 address, such as &lt;code&gt;http://[2607:f8b0:400d:c03::64]&lt;/code&gt;. The IPv6 loopback address (localhost) is &lt;code&gt;::1&lt;/code&gt;, and IPv6 subnets are written the same way as in IPv4. Given how many IPv6 addresses there are, it’s common to be allocated lots of them in cases when you might have expected to only receive one IPv4 address. Typically these blocks will be anywhere from /48 to /56 - which contains more addresses than the entire IPv4 space.&lt;/p&gt;&lt;p&gt;IP addresses are often &lt;strong&gt;static&lt;/strong&gt;, which means the node connecting to the network already knows its IP address and starts using it right away. They may also be &lt;strong&gt;dynamic&lt;/strong&gt;, and are allocated by some computer on the network with the &lt;strong&gt;DHCP&lt;/strong&gt; protocol.&lt;/p&gt;&lt;p&gt;IPsec also lives in layer 3.&lt;/p&gt;&lt;h2&gt;4: Transport Layer&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Generally implemented by the kernel&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The transport layer is where you have higher level protocols, through which much of the work gets done. Protocols here include TCP, UDP, ICMP (used for ping), and others. These protocols are used to power application-layer protocols.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;TCP&lt;/strong&gt;, or the transmission control protocol, is probably the most popular transport layer protocol out there. It turns the unreliable internet protocol into a reliable byte stream. TCP (tries to) make four major guarantees: data will arrive, will arrive exactly once, will arrive in the correct order, and will be the correct data.&lt;/p&gt;&lt;p&gt;TCP takes a stream of bytes and breaks it up into &lt;strong&gt;segments&lt;/strong&gt;. Each segment is then stuck into an IP packet and sent on its way. A TCP segment includes the source and destination &lt;strong&gt;ports&lt;/strong&gt;, which are used to distinguish between different application-layer protocols in use and to distinguish between different applications using the protocol on the same host; a &lt;strong&gt;sequence number&lt;/strong&gt;, which is used to order the packet; an &lt;strong&gt;ACK number&lt;/strong&gt;, which is used to inform the other end that it has received some packet and it can stop retrying; a checksum; and the data itself. The protocol also includes a handshake process and other housekeeping processes that the application needn’t be aware of. Generally speaking, the overhead of TCP is significant for real-time applications.&lt;/p&gt;&lt;p&gt;Most TCP servers will &lt;strong&gt;bind&lt;/strong&gt; to a certain port to &lt;strong&gt;listen&lt;/strong&gt; for incoming connections, via the operating system’s &lt;strong&gt;socket&lt;/strong&gt; implementation. Many TCP &lt;strong&gt;clients&lt;/strong&gt; can connect to one server.&lt;/p&gt;&lt;p&gt;Ports are a 16 bit unsigned integer. Most applications have a default port they’re known to use, such as 80 for HTTP. Originally these numbers were allocated by the internet police, but this has fallen out of practice. On most systems, ports less than 1024 require elevated permissions to listen to.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;UDP&lt;/strong&gt;, or the user datagram protocol, is the second most popular transport layer protocol, and is the lighter of the two. UDP is a paper thin layer on top of IP. A UDP packet contains a source port, destination port, checksum, and a payload. This protocol is fast and lightweight, but makes none of the promises TCP makes - UDP “&lt;strong&gt;datagrams&lt;/strong&gt;” may arrive multiple or zero times, in a different order than they were sent, and possibly with data errors. Many people who use UDP will implement these guarantees themselves in a some lighter-weight fashion than TCP. Importantly, UDP source IPs can be spoofed and the destination has no means of knowing where it really came from - TCP avoids this by doing a handshake before exchanging any data.&lt;/p&gt;&lt;p&gt;UDP can also issue broadcasts, which are datagrams that are sent to every node on the network. Such datagrams should be addressed to &lt;code&gt;255.255.255.255&lt;/code&gt;. There’s also multicast, which specifies a subset of all nodes to send the datagram to. Note that both of these have limited support in real-world networks.&lt;/p&gt;&lt;h2&gt;5 &amp; 6: Session and presentation&lt;/h2&gt;&lt;p&gt;Think of these as extensions of layer 7, the application layer. Technically things like SSL, compression, etc are done here, but in practice it doesn’t have any important technical implications.&lt;/p&gt;&lt;h2&gt;7: Application layer&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Generally implemented by end-user software&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The application layer is the uppermost layer of the network and it’s what all the other layers are there for. At this layer you have all of the hundreds of thousands of application-specific protocols out there.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;DNS&lt;/strong&gt;, or the domain name system, is a protocol for mapping domain names (i.e. google.com) to IP addresses (i.e. 209.85.201.100), among other features. DNS servers keep track of DNS records, which associate names with records of various types. Common records include A, which maps a name to an IPv4 address, AAAA for IPv6, CNAME for aliases, and MX for email records. The most popular DNS server is bind, which you can run on your own network to operate a private name system.&lt;/p&gt;&lt;p&gt;Some other UDP protocols: NTP, the network time protocol; DHCP, which assigns dynamic IP addresses on networks; and nearly all real-time video and audio streaming protocols (like VoIP). Many video games also use UDP for their multiplayer networking.&lt;/p&gt;&lt;p&gt;TCP is more popular than UDP and powers many, many, many applications, due largely to the fact that it simplifies the complex intricacies of networking. You’re probably familiar with HTTP, which is used by web browsers use to fetch resources. Email applications often communicate over TCP with IMAP to retrieve the contents of your inbox, and SMTP to send emails to other servers. SSH (the secure shell), FTP (file transfer protocol), IRC (internet relay chat), and countless other protocols also use TCP.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;Hopefully this article helps you gain a general understanding of how computers talk to each other. In my own experience, I’ve used a broad understanding of the entire stack and a deep understanding of levels 3 and up. I expect most programmers today need a broad understanding of the entire stack and a deep understanding of level 7, and I hope that most programmers would seek a deep understanding of level 4 as well.&lt;/p&gt;&lt;p&gt;Please leave some feedback if you appreciated this article - I may do more similar articles in the future, giving a broad introduction to other topics. The next topics I have in mind are security and encryption (as separate posts).&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-broad-intro-to-networking/</link>
        
        <pubDate>Tue, 06 Dec 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-broad-intro-to-networking/</guid>
      </item>
    
      <item>
        
        
          <title>Electron considered harmful</title>
          <description>
            &lt;p&gt;Yeah, I know that “considered harmful” essays are allegedly &lt;a href=&quot;http://meyerweb.com/eric/comment/chech.html&quot; target=&quot;_blank&quot;&gt;considered harmful&lt;/a&gt;. If it surprises you that I’m writing one, though, you must be a new reader. Welcome! Let’s get started. If you’re unfamiliar with Electron, it’s some hot new tech that lets you make desktop applications with HTML+CSS+JavaScript. It’s basically a chromeless web browser with a Node.js backend and a Chromium-based frontend. What follows is the rant of a pissed off Unix hacker, you’ve been warned.&lt;/p&gt;&lt;p&gt;As software engineers we have a responsibility to pick the &lt;em&gt;right&lt;/em&gt; tools for the job. In fact, that’s the &lt;em&gt;most important&lt;/em&gt; choice we have to make when we start a project. When you choose Electron you get:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;An entire copy of Chromium you’ll be shipping with your app&lt;/li&gt;&lt;li&gt;An interface that looks and feels nothing like the rest of the user’s OS&lt;/li&gt;&lt;li&gt;One of the slowest, least memory efficient, and most inelegant GUI application platforms out there (remember, we &lt;em&gt;tolerate&lt;/em&gt; frontend web development because we have no choice, not because it is by any means &lt;em&gt;good&lt;/em&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Let’s go over some case studies.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/mifi/lossless-cut&quot; target=&quot;_blank&quot;&gt;lossless-cut&lt;/a&gt;&lt;/strong&gt; is an Electron app that gives you a graphical UI for &lt;em&gt;two ffmpeg flags&lt;/em&gt;. Seriously, the flags in question are -ss and -t. No really, that’s &lt;em&gt;&lt;a href=&quot;https://github.com/mifi/lossless-cut/blob/master/src/ffmpeg.js#L46&quot; target=&quot;_blank&quot;&gt;literally all it does&lt;/a&gt;&lt;/em&gt;. It doesn’t even use ffmpeg to decode the video preview in the app, it’s limited to the codecs chromium supports. It also ships its own ffmpeg, so it has the industry standard video decoding tool &lt;em&gt;right there&lt;/em&gt; and doesn’t use it to render video. For the price of 200 extra MiB of disk space and an entire Chromium process in RAM and on your CPU, you get a less capable GUI that saves you from having to type the -ss and -t flags yourself.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://1clipboard.io/&quot; target=&quot;_blank&quot;&gt;1Clipboard&lt;/a&gt;&lt;/strong&gt; is a clipboard manager. In Electron. A &lt;em&gt;clipboard manager&lt;/em&gt;. In order to show you &lt;em&gt;a list of things you’ve copied&lt;/em&gt;, it uses &lt;em&gt;an entire bundled copy of Chromium&lt;/em&gt;. Also note that despite the promises of Electron making cross platform development easy, it doesn’t support Linux.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://getcollectie.com/&quot; target=&quot;_blank&quot;&gt;Collectie&lt;/a&gt;&lt;/strong&gt; is a… fancy bookmark manager, I guess? Another one that fails to get the cross platform value add from Electron, this only supports OS X (or is it macOS). For only $10 bucks you get to organize your shit into folders. Or you could just open the Finder for free and get a native UX to boot.&lt;/p&gt;&lt;p&gt;This is a &lt;a href=&quot;https://hyper.is/&quot; target=&quot;_blank&quot;&gt;terminal&lt;/a&gt; written with Electron. On the landing page they say “# A terminal emulator 100% based on JavaScript, HTML, and CSS” like they’re proud of it. They’ve taken one of the most lightweight and essential tools on your computer and bloated it by orders of magnitude. Why the fuck would you want to render Google in your god damn terminal emulator? Bonus: also not cross platform.&lt;/p&gt;&lt;p&gt;This is not to mention the dozens of companies that have taken their websites and crammed them into a shitty electron app and called it their desktop app. Come on guys!&lt;/p&gt;&lt;p&gt;By the way, if you’re the guy who’s going to leave a comment about how this blog post introduced you to a bunch of interesting apps you’re going to install now, I hate you.&lt;/p&gt;&lt;h2&gt;Electron enables lazy developers to write garbage&lt;/h2&gt;&lt;p&gt;Let me be clear about this: JavaScript sucks. It’s not the worst, but it’s also not by any means good. ES6 is a really great step forward and I’m thrilled about how much easier it’s going to be to write JavaScript, but it’s still JavaScript underneath the syntactic sugar. We use it because we have no choice (people who know more than just JavaScript know this). The object model is whack and the loose typing is whack and the DOM is super whack.&lt;/p&gt;&lt;p&gt;When Node.js happened, a bunch of developers who never bothered to learn more than JavaScript for their frontend work suddenly could write their crappy code on the backend, too. Now this is happening to desktop applications. The reason people choose Electron is because they are &lt;em&gt;too lazy&lt;/em&gt; to learn the right tools for the job. This is the &lt;em&gt;worst&lt;/em&gt; quality a developer can have. You’re an engineer, for the love of God! Fucking act like one! Do they build square airplanes so they don’t have to learn about aerodynamics, then just throw on an extra ten engines to make up for it? NO!&lt;/p&gt;&lt;p&gt;For the love of God, learn something else. Learn how to use GTK or Qt. Maybe Xwt is more up your alley. How about GNOME’s Vala thing? &lt;em&gt;Learn another programming language&lt;/em&gt;. Learn Python or C/C++ or C#. Fun fact: it’ll make your JavaScript better, and once you have it in your toolbox you can make more educated decisions on the appropriate tool to use when you face your next problem. Hint: it’s not Electron.&lt;/p&gt;&lt;h2&gt;Some Electron apps don’t suck&lt;/h2&gt;&lt;p&gt;For some use-cases Electron is a reasonable choice.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot;&gt;Visual Studio Code&lt;/a&gt;, because it’s a full blown IDE with a debugger and plugins and more. It’s already gonna be bloated.&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.soundnodeapp.com/&quot; target=&quot;_blank&quot;&gt;Soundnode&lt;/a&gt;, because it’s not like any other music service’s app obeys your OS’s UI conventions&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Uh, that’s it. That’s the entire list.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Electron-considered-harmful/</link>
        
        <pubDate>Thu, 24 Nov 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Electron-considered-harmful/</guid>
      </item>
    
      <item>
        
        
          <title>Getting on without Google</title>
          <description>
            &lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/d718.png&quot;&gt;&lt;/p&gt;&lt;p&gt;I used Google for a long time, but have waned myself off of it over the past few years, and I finally deleted my account a little over a month ago. I feel so much better about my privacy now that I’ve removed Google from the equation, and self hosting my things affords me a lot of flexibility and useful customizations.&lt;/p&gt;&lt;h2&gt;mail.cmpwn.com&lt;/h2&gt;&lt;p&gt;This one was the most difficult and time consuming to set up, but it was &lt;em&gt;very&lt;/em&gt; worth it. I’ve intended for a while to make a new mail server software suite that’s less terrible to set up, so hopefully that situation will improve in the future. I want to flesh out &lt;a href=&quot;https://github.com/SirCmpwn/aerc&quot; target=&quot;_blank&quot;&gt;aerc&lt;/a&gt; some more first. A personal mail server was one of the earliest things I set up in my post-Google life - I’ve operated it for about two years now.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Postfix to handle incoming and outgoing mail&lt;/li&gt;&lt;li&gt;Dovecot to handle mail delivery, filtering, and IMAP&lt;/li&gt;&lt;li&gt;Postfixadmin to provide a nice interface for managing accounts&lt;/li&gt;&lt;li&gt;mutt to read and compose my emails on the desktop&lt;/li&gt;&lt;li&gt;K9 to read and compose my emails on Android&lt;/li&gt;&lt;li&gt;Roundcube for when it’s occasionally necessary to read an HTML email&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With my mail server provides a lot of side benefits, too. For one, all of my email-sending software now uses it. Once Mandrill went kaput, it was easy to switch everything over to it. I can be sending and receiving email from a new domain in less than 5 minutes now. Using sieve scripts for filtering emails is also a lot more flexible than what Google offered - I now have filtering set up to organize several mailing lists, alerts and notifications sent by my software and servers, RSS feeds, and more.&lt;/p&gt;&lt;p&gt;My strategy for defeating spam is to use a combination of the spamhaus blocklist, greylisting, and blacklisting with sieve. I see about 3-5 spam emails per week on average with this setup. To ensure my own emails get delivered, I’ve set up SPF and DKIM, reverse DNS, and appealed to have my IP address removed from blocklists. A great tool in figuring all this out has been &lt;a href=&quot;http://mail-tester.com&quot; target=&quot;_blank&quot;&gt;mail-tester.com&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;YouTube&lt;/h2&gt;&lt;p&gt;For YouTube, I “subscribe” to channels by adding their RSS feeds to &lt;a href=&quot;http://www.allthingsrss.com/rss2email/&quot; target=&quot;_blank&quot;&gt;rss2email&lt;/a&gt;, combined with sieve scripts that filter them into a specific folder. I then have a keybinding in mutt that, when pressed, pulls the YouTube URL out of an email and feeds it to mpv, a desktop video player. It’s so much easier to access YouTube this way than through the web browser - no ads, familiar keybindings, remote control support, and a no-nonsense feed of your videos.&lt;/p&gt;&lt;h2&gt;Music&lt;/h2&gt;&lt;p&gt;Instead of Google Music, Spotify, or anything else, I run an internet radio with my friends. We all keep our music collections (mostly lossless) on NFS servers, and we mounted these servers on a streaming server that shuffles the entire thing and keeps a searchable database of music. We have an API that I pull from to integrate desktop keybindings and a status line on my taskbar, and an IRC bot for searching the database and requesting songs. I can also stream to my phone with VLC, as well as use scripts to maintain an offline archive of my favorite songs. This setup is &lt;em&gt;way&lt;/em&gt; nicer than any commercial service I’ve used in the past. We’ll be open sourcing version 2 to provide a turnkey solution for this type of self-hosted music service.&lt;/p&gt;&lt;h2&gt;Web search&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://duckduckgo.com/&quot; target=&quot;_blank&quot;&gt;DuckDuckGo&lt;/a&gt;. Even if you think the search results aren’t up to snuff (you get used to just being a bit more specific anyway), the bangs feature is absolutely indispensable. I recently patched Chromium for Android to support DuckDuckGo as a search engine as well: &lt;a href=&quot;https://drewdevault.com/l.sr.ht/h4bZ.patch&quot;&gt;here’s the patch&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;File hosting&lt;/h2&gt;&lt;p&gt;Instead of using Google Drive, I’m using a number of different solutions depending on what’s most convenient at the time. I operate &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt; for me and my friends, which allows me to just have a place to drop a file and get a link to share. I have scripts and keybindings set up to make uploading files here second nature, as well as an Android app someone wrote. I also keep a 128G flash drive on my keychain now that comes in handy all the time, and a big-ass file server on OVH that I keep mounted with NFS or sshfs depending on the scenario, and sometimes I just stash files on a random server with rsync. sr.ht is &lt;a href=&quot;https://gogs.sr.ht/SirCmpwn/sr.ht&quot; target=&quot;_blank&quot;&gt;open source&lt;/a&gt;, by the way.&lt;/p&gt;&lt;h2&gt;CyanogenMod&lt;/h2&gt;&lt;p&gt;On Android, I use CyanogenMod without Google Play Services, and I use F-Droid to get apps. When I used Google Now, I found that I most often just asked it for reminders, which I now do via an open source app called Notable Plus. I also have open source apps for reading HN, downloading torrents, blocking ads, connecting to IRC, two factor authentication, YouTube, password management, Twitter, and more.&lt;/p&gt;&lt;h2&gt;Notably missing: Docs&lt;/h2&gt;&lt;p&gt;Hopefully the new LibreOffice thing will do the trick once it’s ready. I’m looking forward to that.&lt;/p&gt;&lt;h2&gt;Things I self host that Google doesn’t offer&lt;/h2&gt;&lt;p&gt;I use ZNC to operate an IRC bouncer, which is great because I use IRC &lt;em&gt;a lot&lt;/em&gt;. It keeps logs for me, keeps me always connected, and gives me a number of nice features to work with. I also host a number of simple websites related to IRC to do things like channel stats and rules.&lt;/p&gt;&lt;p&gt;To all sr.ht users I offer access to &lt;a href=&quot;https://gogs.sr.ht&quot; target=&quot;_blank&quot;&gt;gogs.sr.ht&lt;/a&gt;, which I personally use to host many private repositories as well as a number of small projects, and as a kind of staging area for repositories that aren’t quite ready for GitHub yet.&lt;/p&gt;&lt;p&gt;For passwords, I use a tool called &lt;a href=&quot;https://www.passwordstore.org/&quot; target=&quot;_blank&quot;&gt;pass&lt;/a&gt;, which encrypts passwords with my PGP key and stores them in a git repository I keep on gogs.sr.ht, with desktop keybindings to make grabbing them convenient.&lt;/p&gt;&lt;h2&gt;Help me do this!&lt;/h2&gt;&lt;p&gt;Well, that covers most of my major self hosted services. If you’re interested in more detail about how any of this works so you might set something up yourself, feel free to reach out to me by &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;email&lt;/a&gt;, &lt;a href=&quot;https://cmpwn.com/@sir&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;, or IRC (SirCmpwn on any network). I’d be happy to help!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Getting-on-without-Google/</link>
        
        <pubDate>Wed, 16 Nov 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Getting-on-without-Google/</guid>
      </item>
    
      <item>
        
        
          <title>I&apos;m losing faith in America</title>
          <description>
            &lt;p&gt;I recently quit my job at Linode and started looking for something else to do. For the first time in my career, I’m seriously considering opportunities abroad. Sorry for the politically charged post - I promise to get back to tech stuff right away.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/canada.png&quot;&gt;&lt;/p&gt;&lt;p&gt;On November 8th, I’m going to step into the voting booth and will be presented with the following options:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A criminal who cheated her way into a spot on the ballot&lt;/li&gt;&lt;li&gt;An egotistical racist maniac&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The next president of the United States will probably be Hillary Clinton. I’m sure I don’t have to tell you how ridiculous this is. This is a person who has pulled all of the stops to get her name on the ballot, including &lt;em&gt;voter fraud&lt;/em&gt; and disturbing amounts of corruption within the Democratic party.  Not to mention that she’s probably going to start a war with Syria, mess with the already fragile geopolitical relationship we have with Russia, and likely deserves to be incarcerated for mishandling classified information. Say what you will about the Republican party - at least Trump won his nomination fair and square. Bonus: not voting for Hillary is sexist.&lt;/p&gt;&lt;p&gt;Not that I’d prefer it if Trump wins. I have a free sandwich waiting for me at the deli nearby if he doesn’t win. He got his nomination fairly, but that doesn’t mean he deserves it. This is a guy with little political clout who is incapable of handling international relations or commanding our military. He staunchly advocates committing war crimes to deal with ISIS. He makes racist, sweeping generalizations about anyone different from him. He’s a misogynist. Even worse, he’s all of these things and seems to actually represent a fair portion of his supporters.&lt;/p&gt;&lt;p&gt;Neither of the independents are serious contenders, so I won’t bother with why I don’t like them. They haven’t earned my vote, either.&lt;/p&gt;&lt;p&gt;Congress is composed of many of the same sort of people. Corrupt politicians who answer to the checkbooks of lobbyists who work against the interests of the American people for the sake of their own. We’re facing climate change and our politicians are taking money from rich fossil fuel lobbyists and damning our species to extinction. The wealth gap between the rich and the poor grows deeper and deeper as absurdly rich people get absurdly richer at the expense of the poor and middle class - through the support of the politicians whose pockets they’ve greased. Their excess wealth could pay for programs to improve our failing infrastructure and provide hundreds of thousands of jobs in doing so. We could provide free healthcare for all Americans too, if it wasn’t for the ongoing debate about whether or not being alive and healthy is a fundamental human right - many thanks to the pharmaceutical interests for shaping this debate to maximize their profits. It’d be less of a problem if many companies weren’t getting rich off of the ever widening waistlines of Americans, too.&lt;/p&gt;&lt;p&gt;Mass surveillance remains in full effect even years after Snowden’s revelations. The ridiculous war on drugs keeps putting people behind bars for lifetimes for victimless crimes to support the financial needs of private prisons and local police departments, who themselves are now better armed than most militaries, based on drug policies that have no basis in reality. 97% of trails end in plea bargains instead of justice, and minimum sentences ensure these people spend ridiculous amounts of time in prisons that punish them rather than rehabilitate them into productive citizens. A judge will hold a defendant indefinitely in prison without a conviction for refusing to disclose their disk encryption password in accordance with their 5th amendment rights - though if many political players had their way, encryption would be illegal anyway.&lt;/p&gt;&lt;p&gt;There’s a word for what America is: &lt;strong&gt;corrupt&lt;/strong&gt;. What the fuck is going on in this country? We aren’t a representative democracy by any stretch of the imagination. We have become an oligarchy. We are ruled by money.&lt;/p&gt;&lt;p&gt;I love America, honestly. My whole family is here and I connect most with the American people. We have an incredibly rich land and great cities full of great innovators and interesting people. I hate that it’s become what it is today. I don’t expect anywhere else to be perfect, but we should be ashamed of how we look next to some other countries out there.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Losing-faith-in-America/</link>
        
        <pubDate>Sat, 05 Nov 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Losing-faith-in-America/</guid>
      </item>
    
      <item>
        
        
          <title>Using the right tool for the job</title>
          <description>
            &lt;p&gt;One of the most important choices you’ll make for the software you write is what you write it in, what frameworks you use, the design methodologies to subscribe to, and so on. This choice doesn’t seem to get the respect it’s due. These are some of the only choices you’ll make that &lt;em&gt;you cannot change&lt;/em&gt;. Or, at least, these choices are among the most difficult ones to change.&lt;/p&gt;&lt;p&gt;People often question why TrueCraft is written in C# next to projects like Sway in C, alongside KnightOS in Assembly or sr.ht in Python. It would certainly be easier from the outset if I made every project in a language I’m comfortable with, using tools and libraries I’m comfortable with, and there’s certainly something to be had for that. That’s far from being the only concern, though.&lt;/p&gt;&lt;p&gt;A new project is a &lt;em&gt;great&lt;/em&gt; means of learning a new language or framework - the only effective means, in fact. However, the inspiration and drive for new projects doesn’t come often. I think that the opportunity for learning is more important than the short term results of producing working code more quickly. Making a choice that’s more well suited to the problem at the expense of comfort will also help your codebase in the long run. Why squander the opportunity to choose something unfamiliar when you have the rare opportunity to start working on a new project?&lt;/p&gt;&lt;p&gt;I’m not advocating for you to use something new for every project, though. I’m suggesting that you detatch your familiarity with your tools from the decision-making process. I often reach for old tools when starting a new project, but I have learned enough about new tools that I can judge what projects are a good use-case for them. Sometimes this doesn’t work out, too - I just threw away and rewrote a prototype in C after deciding that it wasn’t a good candidate for Rust.&lt;/p&gt;&lt;p&gt;Often it does work out, though. I’m glad I chose to learn Python for MediaCrush despite having no experience with it (thanks again for the help with that, Jose!). Today I still know it was the correct choice and knowing it has hugely expanded my programming skills, and without that choice there probably wouldn’t have been a Kerbal Stuff or a sr.ht or likely even the new API we’re working on at Linode. I’m glad I chose to learn C for z80e, though I had previously written emulators in C#. Without it there wouldn’t be many other great tools in the KnightOS ecosystem written in C, and there wouldn’t be a Sway or an aerc. I’m glad I learned ES6 and React instead of falling back on the familiar Knockout.js when building prototypes for the new Linode manager as well.&lt;/p&gt;&lt;p&gt;Today, I have a mental model of the benefits and drawbacks of a lot of languages, frameworks, libraries, and platforms I don’t know how to use. I’m sort of waiting for projects that would be well suited to things like Rust or Django or Lisp or even Plan 9. Remember, the skills you already know make for a great hammer, but you shouldn’t nail screws to the wall.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Use-the-right-tool/</link>
        
        <pubDate>Sat, 17 Sep 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Use-the-right-tool/</guid>
      </item>
    
      <item>
        
        
          <title>What motivates the authors of the software you use?</title>
          <description>
            &lt;p&gt;We face an important choice in our lives as technophiles, hackers, geeks: the choice between proprietary software and free/open source software. What platforms we choose to use are important. We have a choice between Windows, OS X, and Linux (not to mention the several less popular choices). We choose between Android or iOS. We choose hardware that requires nonfree drivers or ones that don’t. We choose to store our data in someone else’s cloud or in our own. How do we make the right choice?&lt;/p&gt;&lt;p&gt;I think it’s important to consider the basic motivations behind the software you choose to use. Why did the author write it? What are their goals? How might that influence the future (or present) direction of this software?&lt;/p&gt;&lt;p&gt;In the case of most proprietary software, the motivations are to make money. They make decisions that benefit the company rather than the user. If you’re paying for the software, they might use vendor lock-in strategies to prevent you from having ownership of your data. If you don’t pay for the software, they might place ads on it, sell your personal information, etc. When Cloud Storage Incorporated is sold to Somewhat Less Trustworthy Business, who’s to say that your data is in good hands?&lt;/p&gt;&lt;p&gt;In the case of most open source&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; software, however, things are different. The decisions the developers make are generally working in the interests of the user. In open source, people work as people, not as companies. You can find the name and email address of the person who wrote a particular feature and send them bugs and questions.&lt;/p&gt;&lt;p&gt;An open source Facebook wouldn’t be rearranging and filtering your timeline to best suit their advertisers interests. An open source iCloud would include import and export tools so you can take your data elsewhere if you so choose. An open source phone wouldn’t be loaded with unremovable crapware, and even if it was, you could patch it.&lt;/p&gt;&lt;p&gt;When you install software on Linux, you get cryptographically verified packages from individuals you can trust. You can look up who packaged your software and get to know them personally, or even help them out! You can download the files necessary to build the package from scratch and do so, adding any tweaks and customizations as you wish. You don’t have a human point of contact for Facebook or GMail.&lt;/p&gt;&lt;p&gt;Yes, there is a usability tradeoff. It is often more difficult to use open source software. However, it’s also often more powerful, tweakable, flexible, and hackable.&lt;/p&gt;&lt;p&gt;Next time you decide what software &lt;em&gt;you&lt;/em&gt; should use, ask yourself: does this software serve my interests or someone else’s?&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/What-motivates-the-authors-of-the-software-you-use/</link>
        
        <pubDate>Fri, 09 Sep 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/What-motivates-the-authors-of-the-software-you-use/</guid>
      </item>
    
      <item>
        
        
          <title>Sway 0.9 &amp; One year of Sway</title>
          <description>
            &lt;p&gt;Today marks one year since the &lt;a href=&quot;https://github.com/SirCmpwn/sway/commit/6a33e1e3cddac31b762e4376e29c03ccf8f92107&quot; target=&quot;_blank&quot;&gt;initial commit&lt;/a&gt; of Sway. Over the year since, we’ve written 1,823 commits by 54 authors, totalling 16,601 lines of C (and 1,866 lines of header files). This was written over the course of 515 pull requests and 300 issues. Today, most i3 features are supported. In fact, as of last week, all of the features from the i3 configuration I used before I started working on Sway are now supported by Sway. Today, Sway looks like this (click to expand):&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/ICd5.png&quot;&gt;&lt;/p&gt;&lt;p&gt;For those who are new to the project, &lt;a href=&quot;http://swaywm.org&quot; target=&quot;_blank&quot;&gt;Sway&lt;/a&gt; is an i3-compatible Wayland compositor. That is, your existing &lt;a href=&quot;http://i3wm.org/&quot; target=&quot;_blank&quot;&gt;i3&lt;/a&gt; configuration file will work as-is on Sway, and your keybindings and colors and fonts and for_window rules and so on will all be the same. It’s i3, but for Wayland, plus it’s got some bonus features. Here’s a quick rundown of what’s happened since the &lt;a href=&quot;https://drewdevault.com/blog/State-of-sway/&quot;&gt;previous state of Sway&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Stacked &amp; tabbed layouts&lt;/li&gt;&lt;li&gt;Customizable input acceleration&lt;/li&gt;&lt;li&gt;Mouse support for swaybar&lt;/li&gt;&lt;li&gt;Experimental HiDPI support&lt;/li&gt;&lt;li&gt;New features for swaylock and swaybg&lt;/li&gt;&lt;li&gt;Support for more i3 IPC features&lt;/li&gt;&lt;li&gt;Tracking of the workspace new windows should arrive on&lt;/li&gt;&lt;li&gt;Improved compatibility with i3&lt;/li&gt;&lt;li&gt;Many improvements to the documentation&lt;/li&gt;&lt;li&gt;Hundreds of bug fixes and small improvements&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Since the last State of Sway, we’ve also seen packages land in the official repositories of Gentoo, OpenSUSE Tumbleweed, and NixOS (though the last group warn me that it’s experimental). And now for some updated stats. Here’s the breakdown of &lt;strong&gt;lines of code per author&lt;/strong&gt; for the top ten authors (with the change from the previous state of Sway in parens):&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;4659 (+352)&lt;/td&gt;&lt;td&gt;Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;3024 (-35)&lt;/td&gt;&lt;td&gt;Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;2232 (+53)&lt;/td&gt;&lt;td&gt;taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;1786 (-40)&lt;/td&gt;&lt;td&gt;S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;1090 (+1090)&lt;/td&gt;&lt;td&gt;Zandr Martin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;619 (-63)&lt;/td&gt;&lt;td&gt;Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;525 (-19)&lt;/td&gt;&lt;td&gt;Cole Mickens&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;461 (-54)&lt;/td&gt;&lt;td&gt;minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;365 (-20)&lt;/td&gt;&lt;td&gt;Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;334 (-11)&lt;/td&gt;&lt;td&gt;Kevin Hamacher&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Notably, Zandr Martin has started regular contributions to Sway and brought himself right up to 5th place in a short time, and while he’s still learning C to boot. Not included here are his recent forays into contributing to our dependencies as well. Thanks man! This time around, I also lost a much more respectable line count - only 35 compared to 457 from the last update.&lt;/p&gt;&lt;p&gt;Here’s the total &lt;strong&gt;number of commits per author&lt;/strong&gt; for each of the top ten committers:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;842&lt;/td&gt;&lt;td&gt; Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;239&lt;/td&gt;&lt;td&gt; Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;186&lt;/td&gt;&lt;td&gt; taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;97&lt;/td&gt;&lt;td&gt; Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;91&lt;/td&gt;&lt;td&gt; S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;58&lt;/td&gt;&lt;td&gt; Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;48&lt;/td&gt;&lt;td&gt; Zandr Martin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt; minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt; David Eklov&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt; Mykyta Holubakha&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most of what I do for Sway personally is reviewing and merging pull requests. Here’s the same figures using &lt;strong&gt;number of commits per author, excluding merge commits&lt;/strong&gt;, which changes my stats considerably:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;383&lt;/td&gt;&lt;td&gt; Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;224&lt;/td&gt;&lt;td&gt; Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;170&lt;/td&gt;&lt;td&gt; taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;96&lt;/td&gt;&lt;td&gt; Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;91&lt;/td&gt;&lt;td&gt; S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;58&lt;/td&gt;&lt;td&gt; Christoph Cysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;38&lt;/td&gt;&lt;td&gt; Zandr Martin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt; minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt; David Eklov&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt; Mykyta Holubakha&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These stats only cover the top ten in each, but there are more - check out the &lt;a href=&quot;https://github.com/SirCmpwn/sway/graphs/contributors&quot; target=&quot;_blank&quot;&gt;full list&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Sway is still going very strong, and continues developing at a fast pace. I’ve updated &lt;a href=&quot;http://swaywm.org/roadmap&quot; target=&quot;_blank&quot;&gt;the roadmap&lt;/a&gt; with our plans for Sway 1.0. You might notice a few features have been reprioritized here, which increases the scope of Sway 1.0. It’ll be worth it, though, to make sure we have a solid 1.0 release. Hopefully we’ll see that and more within the year ahead!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Sway-0.9-in-retro/</link>
        
        <pubDate>Tue, 02 Aug 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Sway-0.9-in-retro/</guid>
      </item>
    
      <item>
        
        
          <title>Using -Wl,--wrap for mocking in C</title>
          <description>
            &lt;p&gt;One of the comforts I’ve grown used to in higher level languages when testing my code is mocking. The idea is that in order to test some code in isolation, you should “mock” the behavior of things it depends on. Let’s see a (contrived) example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;read_to_end&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;) {
    &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (!&lt;span class=&quot;constant variable function&quot;&gt;feof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;)) {
        &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;fread&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;256&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we want to test this function without mocking, we would need to actually open a specially crafted file and provide a &lt;code&gt;FILE*&lt;/code&gt; to the function. However, with the linker &lt;code&gt;--wrap&lt;/code&gt; flag, we can define a wrapper function. Using &lt;code&gt;-Wl,[flag]&lt;/code&gt; in your C compiler command line will pass &lt;code&gt;[flag]&lt;/code&gt; to the linker. Gold (GNU) and lld (LLVM) both support the wrap flag, which specifies a function to be “wrapped”. If I use &lt;code&gt;-Wl,--wrap=fread&lt;/code&gt;, then the code above will be compiled like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;read_to_end&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;) {
    &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (!&lt;span class=&quot;constant variable function&quot;&gt;feof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;)) {
        &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;__wrap_fread&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;256&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if I add &lt;code&gt;-Wl,--wrap=feof&lt;/code&gt; we’ll get this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;read_to_end&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;) {
    &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (!&lt;span class=&quot;constant variable function&quot;&gt;__wrap_feof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;)) {
        &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;__wrap_fread&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;256&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can define some functions that do the behavior we need to test instead of invoking fread directly:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;feof_return_value&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;__wrap_feof&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt;) {
    &lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x1234&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;feof_return_value&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;test_read_to_end_eof&lt;/span&gt;() {
    &lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;constant variable&quot;&gt;feof_return_value&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable function&quot;&gt;read_to_end&lt;/span&gt;((&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x1234&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;buf&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;--wrap&lt;/code&gt; also conveniently defines &lt;code&gt;__real_feof&lt;/code&gt; and &lt;code&gt;__real_fread&lt;/code&gt; if we need them.&lt;/p&gt;&lt;p&gt;Unfortunately, you can’t have two different wrappers for the same function in an executable. This could lead to having to write several executables for each, or making your wrapper function smart enough to have several configurable outcomes.&lt;/p&gt;&lt;p&gt;Eventually I intend to write my own test framework for C, which will use wrappers to support mocking. I want wrappers to be done automatically and have it behave something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;fake_fread&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;,
        &lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;nmemb&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;stream&lt;/span&gt;) {
    &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;hello&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;Hello world!&amp;quot;&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable function&quot;&gt;memcpy&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;hello&lt;/span&gt;, &lt;span class=&quot;constant variable function&quot;&gt;strlen&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;hello&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;strlen&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;hello&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;test_read_to_end&lt;/span&gt;() {
    &lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x1234&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;buffer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;char&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;1024&lt;/span&gt;]&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;type&quot;&gt;mock_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mock_feof&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;configure_mock&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;feof&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;p&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable&quot;&gt;mock_feof&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;call&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;returns&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable&quot;&gt;mock_feof&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;returns&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;// pzzp is pointer, size_t, size_t, pointer&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;// Tells us what the fread arguments look like&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;mock_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;mock_fread&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;configure_mock&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;fread&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;pzzp&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable&quot;&gt;mock_fread&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;exec&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;fake_fread&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;constant variable function&quot;&gt;read_to_end&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;test&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;buffer&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;mock_feof&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;call_count&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;((&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;mock_feof&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;call&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;args&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;test&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;mock_fread&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;call_count&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;((&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;mock_fread&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;call&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;args&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;buffer&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;((&lt;span class=&quot;type&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;mock_fread&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;call&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;args&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;test&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;constant variable function&quot;&gt;assert&lt;/span&gt;(&lt;span class=&quot;constant variable function&quot;&gt;strcmp&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;buffer&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;Hello world!&amp;quot;&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Using-Wl-wrap-for-mocking-in-C/</link>
        
        <pubDate>Tue, 19 Jul 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Using-Wl-wrap-for-mocking-in-C/</guid>
      </item>
    
      <item>
        
        
          <title>Life, liberty, and the pursuit of privacy</title>
          <description>
            &lt;p&gt;Privacy is my hobby, and should be a hobby of every technically competent American. Within the eyes of the law I have a right to secure the privacy of my information. At least that’s the current law - many officials are &lt;a href=&quot;http://www.apple.com/customer-letter/&quot; target=&quot;_blank&quot;&gt;trying to subvert that right&lt;/a&gt;. I figure that we’d better exercise that right while we have it, so that we know how to keep exercising it once it’s illegal and all the information about it dries up.&lt;/p&gt;&lt;p&gt;One particularly annoying coworker often brings up, “what do you have to hide?” Though it would defeat the purpose to explain what I’m hiding, let’s assume that what I’m hiding is benign, at least legally speaking. I’m sure you can understand why I don’t want &lt;code&gt;~/Porn&lt;/code&gt; to be public information should my equipment be seized after I publish this blog post and an incompetent (or angry) investigator leaks it. Building secure facilities for housing secrets is fun! That’s true even if there aren’t a lot of interesting secrets to hide there.&lt;/p&gt;&lt;p&gt;But the porn folder brings up an interesting point. I’m not ashamed to admit I have one, but I would be uncomfortable with everyone being able to see it. Or maybe I’m having an affair (a scandalous proposition for a single guy, I know) and there are relevant texts are on my cell phone. Perhaps I suck at managing my finances and the spreadsheets in my documents would tell you so. Maybe I have embarrassing home videos of bedroom activities on my hard drive&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Maybe there’s evidence that I’m a recovering alcoholic in my files. Maybe I’m a closeted homosexual and my files prove it, and 10 years from now the homophobes win and suddenly the country is more hostile to that. Maybe all of this is true at once!&lt;/p&gt;&lt;p&gt;Keeping these things secret is an important right, and one I intend to exercise. I don’t want to be accused of some crime and have my equipment seized and then mishandled by incompetent officials and made public. I don’t want a jury chosen to decide if I really stole that pack of gum when I was 8 and then have unfavorable secrets leaked. Human nature might lead them to look on my case unfavorably if they found out about all the tentacle porn or erotic Harry Potter fanfics I’ve been secretly writing. Maybe an investigator finds something they don’t understand, like a private key, and it ends up being exposed through the proceedings. Maybe this private key proves that I’m Satoshi Nakamoto&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and my life is threatened when the case is closed because of it.&lt;/p&gt;&lt;p&gt;To the government: &lt;strong&gt;stay the fuck out of my right to encrypt&lt;/strong&gt;, or, as I like to think of it, my right to use math. They will try, again and again, to take it from us. They must never win.&lt;/p&gt;&lt;p&gt;The second act of this blog post is advice on how to go about securing your privacy. The crucial bit of advice is that you must strive to understand the systems you use for privacy and security. Look for their weak spots and be aware of them. Don’t deceive yourself about how secure your systems are.&lt;/p&gt;&lt;p&gt;I try to identify pain points in my security model. Some of them will be hard to swallow. The first one was Facebook - delete your account&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. I did this years ago. The second one was harder still - Google. I use an Android phone running CyanogenMod without Google Play Services. I also don’t use GMail or any Google services (I search with DuckDuckGo and add !sp to use StartPage if necessary). Another one was not using Windows or OS X. This is easy for me but a lot of people will bitch and moan about it. A valid privacy &amp; security model does not include Windows. OS X is an improvement but you’d be better off on Linux. Even your non-technical family can surely figure out how to use Xubuntu to surf the web.&lt;/p&gt;&lt;p&gt;I also use browser extensions to subvert tracking and ads. Ad networks have severely fucked themselves by this point - I absolutely never trust any ads on the web, and never will, period. Use software like &lt;a href=&quot;https://github.com/gorhill/uBlock&quot; target=&quot;_blank&quot;&gt;uBlock&lt;/a&gt; to get rid of trackers (and speed up the web, bonus!). I also block lots of trackers in my /etc/hosts file - &lt;a href=&quot;https://github.com/StevenBlack/hosts&quot; target=&quot;_blank&quot;&gt;check this out&lt;/a&gt;. Also check out &lt;a href=&quot;https://free-software-for-android.github.io/AdAway/&quot; target=&quot;_blank&quot;&gt;AdAway&lt;/a&gt; for Android.&lt;/p&gt;&lt;p&gt;These changes help to remove your need to trust that corporate interests will be good stewards of your private information. This is very important - no amount of encryption will help you if you give Google a GPS map of your every move&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fn-5-ref-1&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; and your search history&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fn-6-ref-1&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; and information about basically every page on the internet you visit&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fn-7-ref-1&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;. And all of your emails and contacts and appointments on your calendar. Google can be subpoenaed or subverted&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-8&quot; id=&quot;fn-8-ref-1&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; and many other companies won’t even try&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-9&quot; id=&quot;fn-9-ref-1&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; to keep your data secret even when they aren’t legally compelled to. I like this image from Maciej Cegłowski’s excellent talk&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-10&quot; id=&quot;fn-10-ref-1&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; on website obesity about the state of most websites:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/ks75.jpg&quot;&gt;&lt;/p&gt;&lt;p&gt;When you give all of this information to Google, Facebook, and others, you’re basically waiving your fifth amendment&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-11&quot; id=&quot;fn-11-ref-1&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; rights.&lt;/p&gt;&lt;p&gt;Once you do have control of your information, there are steps you should take to keep it secure. The answer is encryption. I use &lt;a href=&quot;https://wiki.archlinux.org/index.php/Dm-crypt&quot; target=&quot;_blank&quot;&gt;dm-crypt&lt;/a&gt; which allows me to encrypt my entire hard drive on Linux. I’m prompted for a password on boot and then everything proceeds (and I’ve never noticed any performance issues, for the record).&lt;/p&gt;&lt;p&gt;I also do most of my mobile computing on a laptop running libreboot&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-12&quot; id=&quot;fn-12-ref-1&quot;&gt;12&lt;/a&gt;&lt;/sup&gt; with 100% open source software. The weak point here is that if your hardware is compromised and you don’t know it, they could steal your password. One possible solution is keeping your boot partition and perhaps another key on a flash drive, but this doesn’t fully solve the problem. I suggest looking into things like case intrusion detection and working on being aware of it when your hardware is messed with.&lt;/p&gt;&lt;p&gt;I mentioned earlier that my phone is running CyanogenMod without any of the Google apps. The weak point here is the radio, which is very insecure and likely riddled with vulnerabilities. I intend to build my own phone soon with a Raspberry Pi, where I can have more control over this - things like being able to disconnect power to the radio or disconnect the microphone when not in use will help.&lt;/p&gt;&lt;p&gt;I also self host my email, which was a huge pain in the ass to set up, but is lovely now that I have it. At some point I intend to write a better mail server to make this easier. I use opportunistic PGP encryption for my emails, but I send depressingly few encrypted emails like this due to poor adoption (follow me on &lt;a href=&quot;https://keybase.io/sircmpwn&quot; target=&quot;_blank&quot;&gt;keybase&lt;/a&gt;? I’ll give you an invitation if you send me an encrypted email asking for one!)&lt;/p&gt;&lt;p&gt;If you have any questions about how to implement any of this, help identifying the weaknesses in your setup, or anything else, please feel free to reach out to me via email (&lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;drew@ddevault.org&lt;/a&gt;+&lt;a href=&quot;https://drewdevault.com/publickey.txt&quot; target=&quot;_blank&quot;&gt;F4EA1B88&lt;/a&gt;) or &lt;a href=&quot;https://twitter.com/sircmpwn&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt; or whatever. Good luck sticking it to the man!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Privacy-as-a-hobby/</link>
        
        <pubDate>Wed, 29 Jun 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Privacy-as-a-hobby/</guid>
      </item>
    
      <item>
        
        
          <title>Understanding pointers</title>
          <description>
            &lt;style&gt;
table {
  border-spacing: 0;
  width: 100%;
}

th, td {
  border-bottom: 1px solid black;
}
&lt;/style&gt;
&lt;p&gt;I was recently chatting with a new contributor to Sway who is using the project as a means of learning C, and he had some questions about what &lt;code&gt;void**&lt;/code&gt; meant when he found some in the code. It became apparent that this guy only has a basic grasp on pointers at this point in his learning curve, and I figured it was time for another blog post - so today, I’ll explain pointers.&lt;/p&gt;&lt;p&gt;To understand pointers, you must first understand how memory works. Your RAM is basically a flat array of &lt;a href=&quot;https://en.wikipedia.org/wiki/Octet_(computing)&quot; target=&quot;_blank&quot;&gt;octets&lt;/a&gt;. Your compiler describes every data structure you use as a series of octets. For the context of this article, let’s consider the following memory:&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;th align=&apos;left&apos;&gt;0x0000&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0001&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0002&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0003&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0004&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0005&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0006&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0007&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x08&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x42&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;We can refer to each element of this array by its index, or address. For example, the value at address 0x0004 is 0x08. On this system, we’re using 16-bit addresses to refer to 8-bit values. On an i686 (32-bit) system, we use 32-bit addresses to refer to 8-bit values. On an amd64 (64-bit) system, we use 64-bit addresses to refer to 8-bit values. On Notch’s imaginary DCPU-16 system, we use 16-bit addresses to refer to 16-bit values.&lt;/p&gt;&lt;p&gt;To refer to the value at 0x0004, we can use a pointer. Let’s declare it like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x0004&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we’re declaring a variable named value, whose type is &lt;code&gt;uint8_t*&lt;/code&gt;. The * indicates that it’s a pointer. Now, because this is a 16-bit system, the size of a pointer is 16 bits. If we do this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;%d\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;value&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will print 2, because it takes 16-bits (or 2 bytes) to refer to an address on this system, even though the value there is 8 bits. On your system it would probably print 8, or maybe 4 if you’re on a 32-bit system. We could also do this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0x0004&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case we’re not casting the &lt;code&gt;uint16_t&lt;/code&gt; value 0x0004 to a &lt;code&gt;uint8_t&lt;/code&gt;, which would truncate the integer. No, instead, we’re casting it to a &lt;code&gt;uint8_t*&lt;/code&gt;, which is the size required to represent a pointer on this system. All pointers are the same size.&lt;/p&gt;&lt;h2&gt;Dereferencing pointers&lt;/h2&gt;&lt;p&gt;We can refer to the value at the other end of this pointer by &lt;em&gt;dereferencing&lt;/em&gt; it. The pointer is said to contain a &lt;em&gt;reference&lt;/em&gt; to a value in memory. By &lt;em&gt;dereferencing&lt;/em&gt; it, we can obtain that value. For example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x0004&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;%d\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;value&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// prints 8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Working with multi-byte values&lt;/h2&gt;&lt;p&gt;Even though memory is basically a big array of &lt;code&gt;uint8_t&lt;/code&gt;, thankfully we can work with other kinds of data structures inside of it. For example, say we wanted to store the value 0x1234 in memory. This doesn’t fit in 8 bits, so we need to store it at two different addresses. For example, we could store it at 0x0006 and 0x0007:&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;th align=&apos;left&apos;&gt;0x0000&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0001&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0002&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0003&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0004&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0005&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0006&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0007&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x08&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x42&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x34&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x12&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;*0x0007 makes up the first byte of the value, and *0x0006 makes up the second byte of the value.&lt;/p&gt;&lt;div class=&quot;well&quot;&gt;
    Why not the other way around? Well, most systems these days use the &quot;little
    endian&quot; notation for storing multi-byte integers in memory, which stores the
    least significant byte first. The least significant byte is the one with the
    smallest order of magnitude (in base sixteen). To get the final number, we
    use (0x12 * 0x100) + (0x34 * 0x1), which gives us 0x1234. Read more about
    endianness &lt;a href=&quot;https://en.wikipedia.org/wiki/Endianness&quot;&gt;here&lt;/a&gt;.
&lt;/div&gt;
&lt;p&gt;C allows us to use pointers that refer to these sorts of composite values, like so:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x0006&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;0x%X\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;value&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// Prints 0x1234&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we’ve declared a pointer to a value whose type is &lt;code&gt;uint16_t&lt;/code&gt;. Note that the size of this pointer is the same size of the &lt;code&gt;uint8_t*&lt;/code&gt; pointer - 16 bits, or two bytes. The value it &lt;em&gt;references&lt;/em&gt;, though, is a different type than &lt;code&gt;uint8_t*&lt;/code&gt; references.&lt;/p&gt;&lt;h2&gt;Indirect pointers&lt;/h2&gt;&lt;p&gt;Here comes the crazy part - you can work with pointers to pointers. The address of the &lt;code&gt;uint16_t&lt;/code&gt; pointer we’ve been talking about is 0x0006, right? Well, we can store that number in memory as well. If we store it at 0x0002, our memory looks like this:&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;th align=&apos;left&apos;&gt;0x0000&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0001&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0002&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0003&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0004&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0005&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0006&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0007&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x06&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x08&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x42&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x34&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x12&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;The question might then become, how do we get it out again? Well, we can use a pointer &lt;em&gt;to that pointer&lt;/em&gt;! Check out this code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pointer_to_a_pointer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x0002&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code just declared a variable whose type is &lt;code&gt;uint16_t**&lt;/code&gt;, which a pointer whose value is a &lt;code&gt;uint16_t*&lt;/code&gt;, which itself points to a value that is a &lt;code&gt;uint16_t&lt;/code&gt;. Pretty cool, huh?  We can dereference this too:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pointer_to_a_pointer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x0002&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pointer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pointer_to_a_pointer&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;0x%X\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pointer&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// Prints 0x1234&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don’t actually even need the intermediate variable. This works too:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pointer_to_a_pointer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x0002&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;0x%X\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pointer_to_a_pointer&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// Prints 0x1234&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Void pointers&lt;/h2&gt;&lt;p&gt;The next question that would come up to your average C programmer would be, “well, what is a &lt;code&gt;void*&lt;/code&gt;?” Well, remember earlier when I said that all pointers, regardless of the type of value they reference, are just fixed size integers? In the imaginary system we’ve been talking about, pointers are 16-bit addresses, or indexes, that refer to places in RAM. On the system you’re reading this article on, it’s probably a 64-bit integer. Well, we don’t actually need to specify the type to be able to manipulate pointers if they’re just a fixed size integer - so we don’t have to. A &lt;code&gt;void*&lt;/code&gt; stores an arbitrary address without bringing along any type information. You can later &lt;em&gt;cast&lt;/em&gt; this variable to a specific kind of pointer to dereference it. For example:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;pointer&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x0006&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;constant variable&quot;&gt;pointer&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;0x%X&amp;quot;&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;uintptr&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// prints 0x34&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take a closer look at this code, and recall that 0x0006 refers to a 16-bit value from the previous section. Here, though, we’re treating it as an 8-bit value - the &lt;code&gt;void*&lt;/code&gt; contains no assumptions about what kind of data is there. The result is that we end up treating it like an 8-bit integer, which ends up being the least significant byte of 0x1234;&lt;/p&gt;&lt;h2&gt;Dereferencing structures&lt;/h2&gt;&lt;p&gt;In C, we often work with structs. Let’s describe one to play with:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;coordinates&lt;/span&gt; {
    &lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;x&lt;/span&gt;, &lt;span class=&quot;property&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;coordinates&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our structure describes a linked list of coordinates. X and Y are the coordinates, and next is a pointer to the next set of coordinates in our list. I’m going to drop two of these in memory:&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;th align=&apos;left&apos;&gt;0x0000&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0001&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0002&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0003&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0004&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0005&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0006&lt;/th&gt;&lt;th align=&apos;left&apos;&gt;0x0007&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&apos;left&apos;&gt;0xAD&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0xDE&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0xEF&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0xBE&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x06&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x00&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x34&lt;/td&gt;&lt;td align=&apos;left&apos;&gt;0x12&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Let’s write some C code to reason about this memory with:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;coordinates&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;coords&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;coordinates&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x0000&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we look at this structure in memory, you might already be able to pick out the values. C is going to store the fields of this struct in order. So, we can expect the following:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;0x%X, 0x%X&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;x&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;y&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To print out “0xDEAD, 0xBEEF”. Note that we’re using the structure dereferencing operator here, &lt;code&gt;-&amp;gt;&lt;/code&gt;. This allows us to dereference values &lt;em&gt;inside&lt;/em&gt; of a structure we have a pointer to. The other case is this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;0x%X, 0x-X&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;x&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;y&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which only works if &lt;code&gt;coords&lt;/code&gt; is not a pointer. We also have a pointer within this structure named next. You can see in the memory I included above that its address is 0x0004 and its value is 0x0006 - meaning that there’s another &lt;code&gt;struct coordinates&lt;/code&gt; that lives at 0x0006 in memory. If you look there, you can see the first part of it. It’s X coordinate is 0x1234.&lt;/p&gt;&lt;h2&gt;Pointer arithmetic&lt;/h2&gt;&lt;p&gt;In C, we can use math on pointers. For example, we can do this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x1000&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which would make the value of &lt;code&gt;addr&lt;/code&gt; 0x1001. But this is only true for pointers whose type is 1 byte in size. Consider this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x1000&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;addr&lt;/code&gt; becomes 0x1002! This is because ++ on a pointer actually adds &lt;code&gt;sizeof(type)&lt;/code&gt; to the actual address stored. The idea is that if we only added one, we’d be referring to an address that is &lt;em&gt;in the middle&lt;/em&gt; of a uint16_t, rather than the next uint16_t in memory that we meant to refer to. This is also how arrays work. The following two code snippets are equivalent:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x1000&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;%d\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;uint16_t&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;)&lt;span class=&quot;number&quot;&gt;0x1000&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;%d\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;addr&lt;/span&gt;[&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;])&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;NULL pointers&lt;/h2&gt;&lt;p&gt;Sometimes you need to work with a pointer that points to something that may not exist yet, or a resource that has been freed. In this case, we use a NULL pointer. In the examples you’ve seen so far, 0x0000 is a valid address. This is just for simplicity’s sake. In practice, pretty much no modern computer has any reason to refer to the value at address 0. For that reason, we use NULL to refer to an uninitialized pointer. Dereferencing a NULL pointer is generally a Bad Thing and will lead to segfaults. As a fun side effect, since NULL is 0, we can use it in an if statement:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;type&quot;&gt;ptr&lt;/span&gt;) {
    &lt;span class=&quot;comment&quot;&gt;// ptr is valid&lt;/span&gt;
} &lt;span class=&quot;constant variable&quot;&gt;else&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;// ptr is not valid&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope you found this article useful! If you’d like something fun to read next, read about &lt;a href=&quot;http://c2.com/cgi/wiki?ThreeStarProgrammer&quot; target=&quot;_blank&quot;&gt;“three star programmers”&lt;/a&gt;, or programmers who have variables like &lt;code&gt;void***&lt;/code&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Understanding-pointers/</link>
        
        <pubDate>Sat, 28 May 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Understanding-pointers/</guid>
      </item>
    
      <item>
        
        
          <title>In Memoriam - Mozilla</title>
          <description>
            &lt;p&gt;Today we look back to the life of Mozilla, a company that was best known for creating the Firefox web browser. I remember a company that made the web better and more open by providing a browser that was faster and more customizable than anyone had ever seen, and by making that browser free and open source.&lt;/p&gt;&lt;p&gt;I expect many of my readers will be older than I am, but my first memories of Firefox are back in high school with Firefox 3. I fondly remember my discovery of it. Mozilla gave us a faster and more powerful web browser to use on school computers. The other choice was Internet Explorer 6 - but with a flash drive we could run a “portable” version of Firefox instead. Using tabbed web browsing was a clear improvement for usability and I loved installing all sorts of cool add-ons and I’m sure I’ve spent at least a few hours of my life browsing persona themes.&lt;/p&gt;&lt;p&gt;Mozilla continued to improve their web browser, and I loved it. As I grew up and learned more about techology and started making my way into programming I loved it even more. I remember a time when I would tell my friends that I’d gladly appoint Mozilla as the steward of the open internet over the W3C. Firefox continued to evolve and allow for even more customiziability. Firefox truly became a &lt;a href=&quot;http://www.catb.org/~esr/jargon/html/H/hacker.html&quot; target=&quot;_blank&quot;&gt;hacker&lt;/a&gt;’s web browser.&lt;/p&gt;&lt;p&gt;Eventually a new player called Chrome arrived on the scene. It was slick and new and very, very fast. Firefox, on the other hand, appeared to become stagnant. I made the switch to Chrome for a few years. However, to my eventual delight, Mozilla didn’t quit. They kept making Firefox better and faster and continued to win on customizability and continued to fight for the best internet possible. One day I tried Firefox again and I found it to be just as friendly and hackable as it once was, only now it was a speed demon on par with Chrome. I returned to Firefox for several happy years.&lt;/p&gt;&lt;p&gt;Chrome adopted a versioning scheme that made Mozilla nervous. They didn’t like being Firefox 4 next to Chrome 11. They made the first of many compromises when they switched to bumping the major version with each release. Mozilla died in April of 2011.&lt;/p&gt;&lt;p&gt;In Mozilla’s place, a new company appeared and started to build a new browser. This new company had good intentions, but has completely lost the spirit of Mozilla. This new browser is a stain on Mozilla’s legacy - it ships with unremovable nonfree add-ons, removes huge swaths of the original add-on API, includes a cryptographically walled garden for add-ons, and apparently now includes an instant messaging and video conferencing platform.&lt;/p&gt;&lt;p&gt;The new company has been suffering as well. They have sunk enormous time and effort into projects that are doomed from the start. They tried to make a mobile phone OS whose UI was powered by technology that’s been proven to produce an inferior mobile experience (HTML+CSS+JS) using the slowest rendering engine on the market (gecko) on the lowest powered phones on the market. When this predictably failed, they turned their sights towards running it on even lower powered IoT devices. This new company has also announced several times that they are killing off another well established and well loved project (Thunderbird) from the old Mozilla. They also recently struck a deal with another dying company, Yahoo, to make their search engine the default for this “neo-Firefox”.&lt;/p&gt;&lt;p&gt;To the new company that calls itself Mozilla: you do an injustice to the memory of Mozilla. I hope that one day we’ll see the Mozilla of the past return.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/In-Memoriam-Mozilla/</link>
        
        <pubDate>Wed, 11 May 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/In-Memoriam-Mozilla/</guid>
      </item>
    
      <item>
        
        
          <title>State of Sway - April 2016</title>
          <description>
            &lt;p&gt;Since the previous &lt;a href=&quot;https://drewdevault.com/blog/State-of-sway/&quot;&gt;State of Sway&lt;/a&gt;, we have accomplished quite a bit. We are now shipping versioned releases of sway, which include support for window borders, input device configuration, more new features, and many bug fixes and stability improvements. I’m also happy to say that Sway 0.5 has landed in the Arch Linux community repository and I’m starting to hear rumors of it landing in other Linux distros as well. Here’s a quick rundown of what’s happened in the past four months:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Window borders work now&lt;/li&gt;&lt;li&gt;Input devices are configurable&lt;/li&gt;&lt;li&gt;swaybar is much more mature, including support for i3status and i3blocks&lt;/li&gt;&lt;li&gt;swaylock has reached a similar level of maturity&lt;/li&gt;&lt;li&gt;New &lt;code&gt;include&lt;/code&gt; config command to include sub-configs&lt;/li&gt;&lt;li&gt;We have a &lt;a href=&quot;https://github.com/SirCmpwn/sway/blob/master/assets/Sway_Wallpaper_Blue_1920x1080.png&quot; target=&quot;_blank&quot;&gt;default wallpaper&lt;/a&gt; and a logo now&lt;/li&gt;&lt;li&gt;musl libc support has been added&lt;/li&gt;&lt;li&gt;More features of the i3 IPC protocol have been implemented&lt;/li&gt;&lt;li&gt;18 more i3 commands have been implemented&lt;/li&gt;&lt;li&gt;Many improvements to documentation&lt;/li&gt;&lt;li&gt;Hundreds of bug fixes and small improvements&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I’m a particularly big fan of the new include command, which allows me to add this to my config file:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;include ~/.config/sway/config.d/`hostname`/*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The net of this is that it includes a set of configs specific to each machine I run Sway on, which each have a unique output device &amp; input device configuration and several other details, but I can include them all under &lt;a href=&quot;https://gogs.sr.ht/SirCmpwn/dotfiles&quot; target=&quot;_blank&quot;&gt;version control&lt;/a&gt; to keep my dotfiles synced between computers.&lt;/p&gt;&lt;p&gt;Today, sway looks like this:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/me1j.png&quot;&gt;&lt;/p&gt;&lt;p&gt;We’re now making our way towards Sway 1.0. I have put together a roadmap of the things we have done and the things that remain to do for Sway 1.0, which is available on the improved website &lt;a href=&quot;http://swaywm.org/roadmap&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. We are still now moving forward on many of these features, including the most asked for feature: the stacked &amp; tabbed window layouts, which is under development from Mikkel Oscar Lyderik. He’s given me this screenshot to tease you with:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/0CkR.png&quot;&gt;&lt;/p&gt;&lt;p&gt;All of this is only possible thanks to the hard work of dozens of contributors. Here’s the breakdown of &lt;strong&gt;lines of code per author&lt;/strong&gt; for the top ten authors (with the difference from the previous State of Sway in parenthesis):&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;4307 (+3180)&lt;/td&gt;&lt;td&gt;Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;3059 (-457)&lt;/td&gt;&lt;td&gt;Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;2285 (+115)&lt;/td&gt;&lt;td&gt;taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;1826 (+40)&lt;/td&gt;&lt;td&gt;S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;682 (-38)&lt;/td&gt;&lt;td&gt;Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;544 (+544)&lt;/td&gt;&lt;td&gt;Cole Mickens&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;515 (-19)&lt;/td&gt;&lt;td&gt;minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;385 (+185)&lt;/td&gt;&lt;td&gt;Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;345 (+266)&lt;/td&gt;&lt;td&gt;Kevin Hamacher&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;166 (+45)&lt;/td&gt;&lt;td&gt;crondog&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Once again, I’m no longer the author of the most lines of code. Sway now has a grand total of 15,422 lines of C and 2,787 lines of headers. Here’s the total &lt;strong&gt;number of commits per author&lt;/strong&gt; for each of the top 10 committers:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;688&lt;/td&gt;&lt;td&gt; Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;212&lt;/td&gt;&lt;td&gt; Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;191&lt;/td&gt;&lt;td&gt; taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;109&lt;/td&gt;&lt;td&gt; S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;97&lt;/td&gt;&lt;td&gt; Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;58&lt;/td&gt;&lt;td&gt; Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt; minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt; crondog&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt; Yacine Hmito&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt; progandy&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As the maintainer of sway, &lt;em&gt;a lot&lt;/em&gt; of what I do is reviewing and merging contributions from others. So these statistics change a bit if we use &lt;strong&gt;number of commits per author, excluding merge commits&lt;/strong&gt;:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;343&lt;/td&gt;&lt;td&gt; Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;201&lt;/td&gt;&lt;td&gt; Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;175&lt;/td&gt;&lt;td&gt; taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;109&lt;/td&gt;&lt;td&gt; S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;96&lt;/td&gt;&lt;td&gt; Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;58&lt;/td&gt;&lt;td&gt; Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt; minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt; crondog&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt; Yacine Hmito&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt; progandy&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These stats only cover the top ten in each, but there are more - check out the &lt;a href=&quot;https://github.com/SirCmpwn/sway/graphs/contributors&quot; target=&quot;_blank&quot;&gt;full list&lt;/a&gt;. Hopefully next time I write a blog post like this, we’ll be well into the lifetime of Sway 1.0!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/State-of-sway-April-2016/</link>
        
        <pubDate>Wed, 20 Apr 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/State-of-sway-April-2016/</guid>
      </item>
    
      <item>
        
        
          <title>How to write a better bloom filter in C</title>
          <description>
            &lt;p&gt;This is in response to &lt;a href=&quot;http://blog.michaelschmatz.com/2016/04/11/how-to-write-a-bloom-filter-cpp/&quot; target=&quot;_blank&quot;&gt;How to write a bloom filter in C++&lt;/a&gt;, which has good intentions, but is ultimately a less than ideal bloom filter implementation. I put together a better one in C in a few minutes, and I’ll explain the advantages of it.&lt;/p&gt;&lt;p&gt;The important differences are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You bring your own hashing functions&lt;/li&gt;&lt;li&gt;You can add arbitrary data types, not just bytes&lt;/li&gt;&lt;li&gt;It uses bits directly instead of relying on the &lt;code&gt;std::vector&amp;lt;bool&amp;gt;&lt;/code&gt; being space effecient&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I chose C because (1) I prefer it over C++ and (2) I just think it’s a better choice for implementing low level data types, and C++ is better used in high level code.&lt;/p&gt;&lt;p&gt;I’m not going to explain the mechanics of a bloom filter or most of the details of why the code looks this way, since I think the original post did a fine job of that. I’ll just present my alternate implementation:&lt;/p&gt;&lt;h2&gt;Header&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#ifndef&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_BLOOM_H&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#define&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_BLOOM_H&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;stddef.h&amp;gt;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; (&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;hash_function&lt;/span&gt;)(&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;data&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_filter&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;comment&quot;&gt;/* Creates a new bloom filter with no hash functions and size * 8 bits. */&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_create&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;/* Frees a bloom filter. */&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_free&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;/* Adds a hashing function to the bloom filter. You should add all of the
 * functions you intend to use before you add any items. */&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_add_hash&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;hash_function&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;func&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;/* Adds an item to the bloom filter. */&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_add&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;/* Tests if an item is in the bloom filter.
 *
 * Returns false if the item has definitely not been added before. Returns true
 * if the item was probably added before. */&lt;/span&gt;
&lt;span class=&quot;type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_test&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;#endif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;&lt;p&gt;The implementation of this is pretty straightfoward. First, here’s the actual structs behind the opaque bloom_t type:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt; {
    &lt;span class=&quot;type&quot;&gt;hash_function&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_filter&lt;/span&gt; {
    &lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;bits&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The hash functions are a linked list, but this isn’t important. You can make that anything you want. Otherwise we have a bit of memory called “bits” and the size of it. Now, for the easy functions:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_create&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;) {
	&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;calloc&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_filter&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;bits&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;malloc&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;size&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_free&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;) {
		&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;func&lt;/span&gt;) {
			&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;constant variable function&quot;&gt;free&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		}
		&lt;span class=&quot;constant variable function&quot;&gt;free&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;bits&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable function&quot;&gt;free&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These should be fairly self explanatory. The first interesting function is here:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_add_hash&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;, &lt;span class=&quot;type&quot;&gt;hash_function&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;func&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;calloc&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;next&lt;/span&gt;) {
		&lt;span class=&quot;constant variable&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;last&lt;/span&gt;) {
		&lt;span class=&quot;constant variable&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	} &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt; {
		&lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given a hashing function from the user, this just adds it to our linked list of hash functions. There’s a slightly different code path if we’re adding the first function. The functions so far don’t really do anything specific to bloom filters. The first one that does is this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_add&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bits&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;bits&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;) {
		&lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;func&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; %= &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;bits&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; / &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;] |= &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; % &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This iterates over each of the hash functions the user has provided and computes the hash of the data for that function (modulo the size of our bloom filter), then it adds this to the bloom filter with this line:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;constant variable&quot;&gt;bits&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; / &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;] |= &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; % &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This just sets the nth bit of the filter where n is the hash. Finally, we have the test function:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_test&lt;/span&gt;(&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;, &lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;bloom_hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;bits&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;bits&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;) {
		&lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property function&quot;&gt;func&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;item&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; %= &lt;span class=&quot;constant variable&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (!(&lt;span class=&quot;constant variable&quot;&gt;bits&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; / &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; % &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;)) {
			&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; false&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		}
		&lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; true&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function is extremely similar, but instead of setting the nth bit, it checks the nth bit and returns if it’s 0:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (!(&lt;span class=&quot;constant variable&quot;&gt;bits&lt;/span&gt;[&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; / &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;] &lt;span class=&quot;operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; % &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;)) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it! You have a bloom filter with arbitrary data types for insert and user-supplied hash functions. I wrote up some simple test code to demonstrate this, after googling for a couple of random hash functions:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;bloom.h&amp;quot;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;djb2&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;_str&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_str&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;5381&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; ((&lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;)) {
		&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; ((&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;number&quot;&gt;5&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt;) &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;jenkins&lt;/span&gt;(&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;_str&lt;/span&gt;) {
	&lt;span class=&quot;keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;_str&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;type&quot;&gt;unsigned &lt;/span&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;while&lt;/span&gt; (&lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;) {
		&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;number&quot;&gt;10&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; ^= (&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class=&quot;number&quot;&gt;6&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	}
	&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; ^= (&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class=&quot;number&quot;&gt;11&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;+=&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt; &amp;lt;&amp;lt; &lt;span class=&quot;number&quot;&gt;15&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}

&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;main&lt;/span&gt;() {
	&lt;span class=&quot;type&quot;&gt;bloom_t&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;bloom&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;bloom_create&lt;/span&gt;(&lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;bloom_add_hash&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;bloom&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;djb2&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;bloom_add_hash&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;bloom&lt;/span&gt;, &lt;span class=&quot;constant variable&quot;&gt;jenkins&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;Should be 0: %d\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable function&quot;&gt;bloom_test&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;bloom&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;bloom_add&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;bloom&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;)&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;Should be 1: %d\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable function&quot;&gt;bloom_test&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;bloom&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;constant variable function&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;Should (probably) be 0: %d\n&amp;quot;&lt;/span&gt;, &lt;span class=&quot;constant variable function&quot;&gt;bloom_test&lt;/span&gt;(&lt;span class=&quot;constant variable&quot;&gt;bloom&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;world hello&amp;quot;&lt;/span&gt;))&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The full code is available &lt;a href=&quot;https://git.sr.ht/~sircmpwn/bloom/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/How-to-write-a-better-bloom-filter-in-C/</link>
        
        <pubDate>Tue, 12 Apr 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/How-to-write-a-better-bloom-filter-in-C/</guid>
      </item>
    
      <item>
        
        
          <title>Please use text/plain for email</title>
          <description>
            &lt;p&gt;A lot of people have come to hate email, and not without good reason. I don’t hate using email, and I attribute this to better email habits. Unfortunately, most email clients these days lead users into bad habits that probably contribute to the sad state of email in 2016. The biggest problem with email is the widespread use of HTML email.&lt;/p&gt;&lt;p&gt;Compare email to snail mail. You probably throw out most of the mail you get - it’s all junk, ads. Think about the difference between snail mail you read and snail mail you throw out. Chances are, the mail you throw out is flashy flyers and spam that’s carefully laid out by a designer and full of eye candy (kind of like many HTML emails). However, if you receive a letter from a friend it’s probably going to be a lot less flashy - just text on a page. Reading letters like this is pleasant and welcome. Emails should be more like this.&lt;/p&gt;&lt;p&gt;I consider this the basic argument for plaintext emails - they make email better. There are more specific problems with HTML emails that I can give, though (not to mention the fact that I read emails &lt;a href=&quot;https://drewdevault.com/2016/03/22/Integrating-a-VT220-into-my-life.html&quot; target=&quot;_blank&quot;&gt;on this&lt;/a&gt; now).&lt;/p&gt;&lt;h2&gt;What’s wrong with HTML email&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Tracking images&lt;/strong&gt; are images that are included in HTML emails with &lt;img /&gt; tags. These images have URLs with unique IDs in them that hit remote servers and let them know that you opened the email, along with various details about your mail client and such. This is a form of tracking, which many people go to great lengths to prevent with tools like &lt;a href=&quot;https://github.com/gorhill/uBlock&quot; target=&quot;_blank&quot;&gt;uBlock&lt;/a&gt;. Most email clients recognize this, and actually &lt;em&gt;block&lt;/em&gt; images from being shown without explicit user consent. If your images aren’t even being shown, then why include them? Tracking users is evil.&lt;/p&gt;&lt;p&gt;Many &lt;strong&gt;vulnerabilities&lt;/strong&gt; in mail clients also stem from rendering HTML email. Luckily, no mail clients have JavaScript enabled on their HTML email renderers. However, security issues related to HTML emails are still found quite often in mail clients. I don’t want to view this crap (and I don’t).&lt;/p&gt;&lt;p&gt;HTML email also makes &lt;strong&gt;phishing&lt;/strong&gt; much easier. I’ve often received HTML emails with links that hide their true intent by using a different href than their text would suggest (and almost always with a tracking code added, ugh). They are also &lt;strong&gt;incompatible&lt;/strong&gt; with many email-based workflows, such as inline quoting, mailing list participation, and sending &amp; working with source code patches.&lt;/p&gt;&lt;h2&gt;Good habits for plaintext emails&lt;/h2&gt;&lt;p&gt;Some nice things are possible when you choose to use plaintext emails. Remember before when I was comparing emails to snail mail letters? Well, let’s continue those comparisons. Popular email clients of 2016 have thoroughly bastardized email, but here’s what it once was and perhaps what it could be today.&lt;/p&gt;&lt;p&gt;The common mail client today uses the abhorrent “&lt;a href=&quot;https://en.wikipedia.org/wiki/Posting_style#Top-posting&quot; target=&quot;_blank&quot;&gt;top posting&lt;/a&gt;” format, where the entire previous message is dumped underneath your reply. As the usual quote goes:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A: Because it messes up the order in which people normally read text.&lt;/p&gt;&lt;p&gt;Q: Why is top-posting such a bad thing?&lt;/p&gt;&lt;p&gt;A: Top-posting.&lt;/p&gt;&lt;p&gt;Q: What is the most annoying thing in e-mail?&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;A better way to write emails is the same way you write a letter to send via snail mail. Would you photocopy the entire history of your correspondence and staple it to your response? After a while you would start paying more for the weight! Though bandwidth seems cheap now, the habit is still silly. Instead of copying the entire conversation into your email, quote only the relevant parts and respond to them inline. For example, let’s say I receive this email:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Hi Drew!

Could you take a look at the server this afternoon? I think it&amp;apos;s having some
issues with nginx.

I also took care of the upgrades you asked for last night. Sorry it took so
long!

--
John Doe
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The best way to respond to this would be:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Hi John!

&amp;gt;Could you take a look at the server this afternoon? I think it&amp;apos;s having some
&amp;gt;issues with nginx.

No problem. I just had a quick look now and nginx was busted. Should be
working now.

&amp;gt;I also took care of the upgrades you asked for last night. Sorry it took so
&amp;gt;long!

Thanks!

--
Drew DeVault
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;John might follow up with this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;gt;Should be working now.

Yep, seems to be up. Thanks!

--
John Doe
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Much better if you ask me. This works particularly well on mailing lists for open source projects, where you send a patch and reviewers will respond by quoting specific parts of your patch and leaving feedback. Just treat emails like letters!&lt;/p&gt;&lt;h2&gt;Multipart emails&lt;/h2&gt;&lt;p&gt;I think there are nothing but negatives to HTML email. I use &lt;a href=&quot;http://www.mutt.org/&quot; target=&quot;_blank&quot;&gt;mutt&lt;/a&gt; to read email, which doesn’t even render HTML emails and allows me to compose emails with Vim. But if you absolutely insist on using HTML emails, please use &lt;a href=&quot;https://en.wikipedia.org/wiki/MIME#Multipart_messages&quot; target=&quot;_blank&quot;&gt;multipart emails&lt;/a&gt;. If you’re sending automated emails, your programming language likely contains a mechanism to facilitate this. The idea is that you send an alternative text/plain body for your email. Be sure that this body contains all of the information of the HTML version. If you do this, I will at least be willing to read your emails.&lt;/p&gt;&lt;h2&gt;How do I use plaintext emails?&lt;/h2&gt;&lt;p&gt;Your mail client should have an option for composing emails with plaintext. Look through your settings for it and it’ll change the default. Then you’re free! Tell your friends to do the same, and your email life will be happier.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Please-use-text-plain-for-emails/</link>
        
        <pubDate>Mon, 11 Apr 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Please-use-text-plain-for-emails/</guid>
      </item>
    
      <item>
        
        
          <title>Integrating a VT220 into my life</title>
          <description>
            &lt;p&gt;I bought a DEC VT220 terminal a while ago, and put it next to my desk at work. I use it to read emails on mutt now, and it’s actually quite pleasant. There was some setup involved in making it as comfortable as possible, though.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/vt220-long-shot.jpg&quot;&gt;
&lt;figcaption&gt;My desk at work&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;Here’s the terminal up close:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://drewdevault.com/vt220-closeup.jpg&quot;&gt;
&lt;figcaption&gt;The terminal itself&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;h2&gt;Hardware&lt;/h2&gt;&lt;p&gt;First, I have several pieces of hardware involved in this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;VT220 terminal&lt;/li&gt;&lt;li&gt;LK201 keyboard (later made obsolete)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://amzn.com/B00IDSM6BW&quot; target=&quot;_blank&quot;&gt;USB to serial adapter&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://amzn.com/B00066HL50&quot; target=&quot;_blank&quot;&gt;DB9-&gt;DB29 null modem cable&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It took a while to get all of these things, but I was able to get a nice refurbished terminal and a couple of crappy LK201 keyboards. Luckily I was able to eventually remove the need for the keyboard.&lt;/p&gt;&lt;h2&gt;Basic Setup&lt;/h2&gt;&lt;p&gt;Getting this working on Linux is actually pretty simple thanks to decades of backwards compatability. Plug all of the cords together, turn on the machine, and (on Arch, at least) run:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;systemctl start serial-agetty@ttyUSB0.service
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will start up a getty for you to log into on your terminal. For a while I would use the LK201 to log in to this getty and spin up a mail cilent.&lt;/p&gt;&lt;p&gt;I did have to make a couple of changes to &lt;a href=&quot;mailto:serial-agetty@.service&quot; target=&quot;_blank&quot;&gt;serial-agetty@.service&lt;/a&gt;, though:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ExecStart=-/sbin/agetty -h -L 19200 %I vt220
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This specifies the TERM variable as “vt220” and sets the baud rate to 19200. I had to also set the baud rate in the terminal’s settings to 19200 baud as well, to get the fastest possible terminal.&lt;/p&gt;&lt;p&gt;I eventually got into the habit of logging into the terminal with the LK201, then running tmux and attaching to tmux from my desktop session. I would then hide this tmux terminal in the upper left corner of my display, and move my mouse over to it when I wanted to interact with the terminal. This let me use the same keyboard I used for the rest of my computer experience to interact with the VT220, instead of trying to use the LK201 as well. This was a bit annoying, so eventually I did some more customization.&lt;/p&gt;&lt;h2&gt;Removing the keyboard&lt;/h2&gt;&lt;p&gt;I wanted to be able to make everything automatic, so I could just boot my computer and log in normally and treat the VT220 almost like a fourth monitor. I started by automating the process of logging in and running tmux.&lt;/p&gt;&lt;p&gt;First, I created a user for the terminal:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;useradd vt220
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, I wrote a shell script that would serve as the user’s login shell and would start tmux:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; [[ &lt;span class=&quot;operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;TERM&lt;/span&gt; == &lt;span class=&quot;string&quot;&gt;&amp;quot;screen&amp;quot;&lt;/span&gt; ]]
&lt;span class=&quot;keyword&quot;&gt;then&lt;/span&gt;
	&lt;span class=&quot;constant function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/usr/local/bin/login-sircmpwn&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;
	&lt;span class=&quot;constant function&quot;&gt;tmux&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-S&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/var/tmux/vt220.sock&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I made that directory, &lt;code&gt;/var/tmux/&lt;/code&gt;, and made sure both the vt220 user and my normal user had access to it. I also edited my sudoers file so that vt220 could run that command as root:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;vt220 ALL=(ALL) NOASSWD: /usr/local/bin/login-sircmpwn
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I put the script into &lt;code&gt;/usr/local/bin&lt;/code&gt; and added it to &lt;code&gt;/etc/shells&lt;/code&gt;, then made it the login shell for the vt220 user with &lt;code&gt;chsh&lt;/code&gt;. I then moved to my own systemd unit for starting the getty on ttyUSB0, this time with autologin:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Serial Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
BindsTo=dev-%i.device
After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service

# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn&amp;apos;t actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes

[Service]
ExecStart=-/sbin/agetty -a vt220 -h -L 19200 %I vt220
Type=idle
Restart=always
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes

[Install]
WantedBy=getty.target
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The only difference here is that it invokes agetty with &lt;code&gt;-a vt220&lt;/code&gt; to autologin as that user. &lt;code&gt;systemctl enable vtgetty@ttyUSB0.service&lt;/code&gt; makes it so that on boot, the getty would run on ttyUSB0 and autologin as vt220. Then the script from earlier will run tmux, and within tmux will run &lt;code&gt;sudo /usr/local/bin/login-sircmpwn&lt;/code&gt;, which is this shell script:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;until&lt;/span&gt; &lt;span class=&quot;constant function&quot;&gt;who&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;constant function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;sircmpwn&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&amp;gt;&amp;amp;1 &lt;span class=&quot;operator&quot;&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt;
	&lt;span class=&quot;constant function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;done&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-iu&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;sircmpwn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What this does is pretty straightforward - it loops until I log in as sircmpwn, then enters an interactive session with sudo as sircmpwn.&lt;/p&gt;&lt;p&gt;The net of all of this is that now, I can boot up my machine, and when I log in, the VT220 starts up with tmux running and logged in as me. Then I went back to the old way of attaching to this tmux session with a terminal on my desktop session hidden in a corner of the screen. And now I could ditch the clunky old LK201 keyboard!&lt;/p&gt;&lt;h2&gt;Treating the terminal as another output&lt;/h2&gt;&lt;p&gt;I said earlier that my goal was to treat the terminal as a fake “output” that I could switch to from my desktop session just like I switch between my three graphical outputs. I run &lt;a href=&quot;https://github.com/SirCmpwn/sway&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt;, of course, so I decided to add a fake output in sway and see where that went. I made a somewhat complicated &lt;a href=&quot;https://github.com/SirCmpwn/sway/compare/vt220&quot; target=&quot;_blank&quot;&gt;branch&lt;/a&gt; for this purpose, but the important change is here:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;diff&quot;&gt;&lt;span class=&quot;variable_builtin&quot;&gt;diff --git a/sway/handlers.c b/sway/handlers.c&lt;/span&gt;
index &lt;span class=&quot;constant&quot;&gt;cec6319&lt;/span&gt;..&lt;span class=&quot;constant&quot;&gt;60f8406&lt;/span&gt; 100644
&lt;span class=&quot;keyword&quot;&gt;--- a/sway/handlers.c&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+++ b/sway/handlers.c&lt;/span&gt;
&lt;span class=&quot;attribute&quot;&gt;@@ -704,6 +704,21 @@ static void handle_wlc_ready(void) {&lt;/span&gt;
 		free(line);
 		list_del(config-&amp;gt;cmd_queue, 0);
 	}
&lt;span class=&quot;string&quot;&gt;+	// VT220 stuff&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	// Adds a made up output that we can use for a tmux window&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	// connected to my vt220&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	swayc_t *output = new_swayc(C_OUTPUT);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	output-&amp;gt;name = &amp;quot;VT220&amp;quot;;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	output-&amp;gt;handle = UINTPTR_MAX;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	output-&amp;gt;width = 1000;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	output-&amp;gt;height = 1000;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	output-&amp;gt;unmanaged = create_list();&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	output-&amp;gt;bg_pid = -1;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	add_child(&amp;amp;root_container, output);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	output-&amp;gt;x = -1000;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	output-&amp;gt;y = 0;&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	new_workspace(output, &amp;quot;__VT220&amp;quot;);&lt;/span&gt;
&lt;span class=&quot;string&quot;&gt;+	// End VT220 stuff&lt;/span&gt;
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a fake output and puts it to the far left, then adds a workspace to it called __VT220. I assigned it the output handle of UINTPTR_MAX and everywhere in sway that it would try to use the output handle to manipulate a real output, I changed to to avoid doing so if the handle is UINTPTR_MAX. Then I added this to my sway config:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;for_window [title=&amp;quot;__VT220&amp;quot;] move window to workspace __VT220 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And run this command when sway starts:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;urxvt -T &amp;quot;__VT220&amp;quot; -e tmux -S /var/tmux/vt220.sock a
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which spawns a terminal whose window title is __VT220 running tmux attached to the session running on the terminal. The for_window rule I added to my sway config automatically moves this to the VT220 fake output and tada! It works. Now I have a nice and comfortable way to use my terminal to read emails at work. Now if only I could convince people to stop sending me HTML emails! I just bought a second VT220 for use at home, too. Life’s good~&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=11339909&quot; target=&quot;_blank&quot;&gt;Discussion on Hacker News&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Integrating-a-VT220-into-my-life/</link>
        
        <pubDate>Tue, 22 Mar 2016 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Integrating-a-VT220-into-my-life/</guid>
      </item>
    
      <item>
        
        
          <title>State of Sway - December 2015</title>
          <description>
            &lt;p&gt;I wrote sway’s &lt;a href=&quot;https://github.com/SirCmpwn/sway/commit/6a33e1e3cddac31b762e4376e29c03ccf8f92107&quot; target=&quot;_blank&quot;&gt;initial commit&lt;/a&gt; 4 months ago, on August 4th. At the time of writing, there are now 1,070 commits from 29 different authors, totalling 10,682 lines of C (and 1,176 lines of header files). This has been done over the course of 256 pull requests and 118 issues. Of the 73 &lt;a href=&quot;https://github.com/SirCmpwn/sway/issues/2&quot; target=&quot;_blank&quot;&gt;i3 features we’re tracking&lt;/a&gt;, 51 are now supported, and I’ve been using sway as my daily driver for a while now. Today, sway looks like this:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/l.sr.ht/NCx_.png&quot;&gt;&lt;/p&gt;&lt;p&gt;For those who are new to the project, &lt;a href=&quot;https://github.com/SirCmpwn/sway&quot; target=&quot;_blank&quot;&gt;sway&lt;/a&gt; is an i3-compatible Wayland compositor. That is, your existing &lt;a href=&quot;http://i3wm.org/&quot; target=&quot;_blank&quot;&gt;i3&lt;/a&gt; configuration file will work as-is on sway, and your keybindings will be the same and the colors and font configuration will be the same, and so on. It’s i3, but on Wayland.&lt;/p&gt;&lt;p&gt;Sway initially made the rounds on &lt;a href=&quot;https://redd.it/3he5hn&quot; target=&quot;_blank&quot;&gt;/r/linux&lt;/a&gt; and &lt;a href=&quot;https://redd.it/3he48j&quot; target=&quot;_blank&quot;&gt;/r/i3wm&lt;/a&gt; and &lt;a href=&quot;https://www.phoronix.com/scan.php?page=news_item&amp;px=Wayland-i3-Sway-Tiling&quot; target=&quot;_blank&quot;&gt;Phoronix&lt;/a&gt; on August 17th, 13 days after the initial commit. I was already dogfooding it by then, but now I’m actually using it 100% of the time, and I hear others have started to as well. What’s happened since then? Well:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Floating windows&lt;/li&gt;&lt;li&gt;Multihead support&lt;/li&gt;&lt;li&gt;XDG compliant config&lt;/li&gt;&lt;li&gt;Fullscreen windows&lt;/li&gt;&lt;li&gt;gaps&lt;/li&gt;&lt;li&gt;IPC&lt;/li&gt;&lt;li&gt;Window criteria&lt;/li&gt;&lt;li&gt;58 i3 commands and 1 command unique to sway&lt;/li&gt;&lt;li&gt;Wallpaper support&lt;/li&gt;&lt;li&gt;Resizing/moving tiled windows with the mouse&lt;/li&gt;&lt;li&gt;swaymsg, swaylock, &lt;strong&gt;swaybar&lt;/strong&gt; as in i3-msg, i3lock, i3bar&lt;/li&gt;&lt;li&gt;Hundreds of bug fixes and small improvements&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Work on sway has also driven improvements in our dependencies, such as &lt;a href=&quot;https://github.com/Cloudef/wlc&quot; target=&quot;_blank&quot;&gt;wlc&lt;/a&gt;, which now has improved xwayland support, support for Wayland protocol extensions (which makes swaybg and swaylock and swaybar possible), and various bugfixes and small features added at the bequest of sway. Special thanks to Cloudef for helping us out with so many things!&lt;/p&gt;&lt;p&gt;All of this is only possible thanks to the hard work of dozens of contributors. Here’s the breakdown of &lt;strong&gt;lines of code per author&lt;/strong&gt; for the top ten authors:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;3516&lt;/td&gt;&lt;td&gt;Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;2400&lt;/td&gt;&lt;td&gt;taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;1786&lt;/td&gt;&lt;td&gt;S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;1127&lt;/td&gt;&lt;td&gt;Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;720&lt;/td&gt;&lt;td&gt;Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;534&lt;/td&gt;&lt;td&gt;minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;200&lt;/td&gt;&lt;td&gt;Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;121&lt;/td&gt;&lt;td&gt;Yacine Hmito&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;79&lt;/td&gt;&lt;td&gt;Kevin Hamacher&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And here’s the total &lt;strong&gt;number of commits per author&lt;/strong&gt; for each of the top 10 committers:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;514&lt;/td&gt;&lt;td&gt; Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;191&lt;/td&gt;&lt;td&gt; taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;102&lt;/td&gt;&lt;td&gt; S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;97&lt;/td&gt;&lt;td&gt; Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt; Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;46&lt;/td&gt;&lt;td&gt; Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt; minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt; Ben Boeckel&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt; Half-Shot&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt; jdiez17&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As the maintainer of sway, &lt;em&gt;a lot&lt;/em&gt; of what I do is reviewing and merging contributions from others. So these statistics change a bit if we use &lt;strong&gt;number of commits per author, excluding merge commits&lt;/strong&gt;:&lt;/p&gt;&lt;table class=&quot;table&quot;&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;279&lt;/td&gt;&lt;td&gt; Drew DeVault&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;175&lt;/td&gt;&lt;td&gt; taiyu&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;102&lt;/td&gt;&lt;td&gt; S. Christoffer Eliesen&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;96&lt;/td&gt;&lt;td&gt; Luminarys&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt; Mikkel Oscar Lyderik&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;46&lt;/td&gt;&lt;td&gt; Christoph Gysin&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt; minus&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt; Ben Boeckel&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt; jdiez17&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt; Yacine Hmito&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These stats only cover the top ten in each, but there are more - check out the &lt;a href=&quot;https://github.com/SirCmpwn/sway/graphs/contributors&quot; target=&quot;_blank&quot;&gt;full list&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;So, what does this all mean for sway? Well, it’s going very well. If you’d like to live on the edge, you can use sway right now and have a productive workflow. The important features that are missing include stacking and tabbed layouts, window borders, and some features on the bar. I’m looking at starting up a beta when these features are finished. Come try out sway! Test it with us, open GitHub issues with your gripes and desires, and &lt;a href=&quot;http://webchat.freenode.net/?channels=sway&amp;uio=d4&quot; target=&quot;_blank&quot;&gt;chat with us on IRC&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;This blog post was composed from sway.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/State-of-sway-December-2015/</link>
        
        <pubDate>Sun, 20 Dec 2015 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/State-of-sway-December-2015/</guid>
      </item>
    
      <item>
        
        
          <title>Bring more Tor into your life</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://www.torproject.org/&quot; target=&quot;_blank&quot;&gt;Tor&lt;/a&gt; is a project that improves your privacy online by encrypting and bouncing your connection through several nodes before leaving for the outside world. It makes it much more difficult for someone spying on you to know who you’re talking to online and what you’re saying to them. Many people use it with the Tor Browser (a fork of Firefox) and only use it with HTTP.&lt;/p&gt;&lt;p&gt;What some people do not know is that Tor works at the TCP level, and can be used for any kind of traffic. There is a glaring issue with using Tor for your daily browsing - it’s significantly slower. That being said, there are several things you run on your computer where speed is not quite as important. I am personally using Tor for several things (this list is incomplete):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;IRC (chat)&lt;/li&gt;&lt;li&gt;Email client&lt;/li&gt;&lt;li&gt;DNS lookups (systemwide)&lt;/li&gt;&lt;li&gt;Downloading system updates&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Anything that supports downloading through a SOCKS proxy can be used through Tor. You can also use programs like &lt;a href=&quot;https://trac.torproject.org/projects/tor/wiki/doc/TorifyHOWTO&quot; target=&quot;_blank&quot;&gt;torify&lt;/a&gt; to transparently wrap syscalls in Tor for any program (this is how I got my email to use Tor).&lt;/p&gt;&lt;p&gt;Of course, Tor can’t help you if you compromise yourself. You should not use bittorrent over Tor, and you should check your other applications. You should also be using SSL/TLS/etc on top of Tor, so that exit nodes can’t be evil with your traffic.&lt;/p&gt;&lt;h2&gt;Orbot&lt;/h2&gt;&lt;p&gt;I also use Tor on my phone. I run all of my phone’s traffic through Tor, since I don’t use the internet on my phone much. I have whitelisted apps that need to stream video or audio, though, for the sake of speed. You can do this, too - set up a black or whitelist of apps on your phone whose networking will be done through Tor. The app for this is &lt;a href=&quot;https://guardianproject.info/apps/orbot/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Why bother?&lt;/h2&gt;&lt;p&gt;The easy answer is “secure everything”. If you don’t have a good reason to remain insecure, you should default to secure. That argument doesn’t work on everyone, though, so here are some others.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Securing trivial traffic makes more noise to hide the things you care about&lt;/li&gt;&lt;li&gt;You can have more peace of mind about using public WiFi networks if you’re using Tor.&lt;/li&gt;&lt;li&gt;ISPs can’t inject extra ads and tracking into things you’re using over Tor.&lt;/li&gt;&lt;li&gt;The NSA targets people who use Tor. If you “have nothing to hide”, then you can help defend those who do by adding more noise and giving agencies that engage in illegal spying a bigger haystack. Bonus: Tor helps make sure that even though you’re being looked at, you’re secure.&lt;/li&gt;&lt;/ul&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Bring-more-tor-into-your-life/</link>
        
        <pubDate>Wed, 11 Nov 2015 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Bring-more-tor-into-your-life/</guid>
      </item>
    
      <item>
        
        
          <title>Please don&apos;t use Slack for FOSS projects</title>
          <description>
            &lt;p&gt;I’ve noticed that more and more projects are using things like Slack as the chat medium for their open source projects. In the past couple of days alone, I’ve been directed to Slack for Babel and Bootstrap. I’d like to try and curb this phenomenon before it takes off any more.&lt;/p&gt;&lt;h2&gt;Problems with Slack&lt;/h2&gt;&lt;p&gt;Slack…&lt;/p&gt;&lt;ul&gt;&lt;li&gt;is closed source&lt;/li&gt;&lt;li&gt;has only one client (&lt;em&gt;update: errata at the bottom of this article&lt;/em&gt;)&lt;/li&gt;&lt;li&gt;is a walled garden&lt;/li&gt;&lt;li&gt;requires users to have a different tab open for each project they want to be involved in&lt;/li&gt;&lt;li&gt;requires that Heroku hack to get open registration&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The last one is a real stinker. Slack is not a tool built for open source projects to use for communication with their userbase. It’s a tool built for teams and it is ill-suited to this use-case. In fact, Slack has gone on record as saying that it &lt;em&gt;cannot&lt;/em&gt; support this sort of use-case: “it’s great that people are putting Slack to good use” but unfortunately “these communities are not something we have the capacity to support given the growth in our existing business.” &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;h2&gt;What is IRC?&lt;/h2&gt;&lt;p&gt;IRC, or Internet Relay Chat…&lt;/p&gt;&lt;ul&gt;&lt;li&gt;is a standardized and well-supported protocol &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;has hundreds of open source clients, servers, and bots &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&lt;li&gt;is a distributed design with several networks&lt;/li&gt;&lt;li&gt;allows several projects to co-exist on the same network&lt;/li&gt;&lt;li&gt;has no hacks for registration and is designed to be open&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;No, IRC is not dead&lt;/h3&gt;&lt;p&gt;I often hear that IRC is dead. Even my dad pokes fun at me for using a 30 year old protocol, but not after I pointed out that he still uses HTTP. Despite the usual shtick from the valley, old is not necessarily a synonym for bad.&lt;/p&gt;&lt;p&gt;IRC has been around since forever. You may think that it’s not popular anymore, but there are still tons of people using it. There are 87,762 users &lt;em&gt;currently online&lt;/em&gt; (at time of writing) on Freenode. There are 10,293 people on OFTC. 22,384 people on Rizon. In other words, it’s still going strong, and I put a lot more faith in something that’s been going full speed ahead since the 80s than in a Silicon Valley fad startup.&lt;/p&gt;&lt;h2&gt;Problems with IRC that Slack solves&lt;/h2&gt;&lt;p&gt;There are several things Slack tries to solve about IRC. They are:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Code snippets&lt;/strong&gt;: Slack has built-in support for them. On IRC you’re just asked to use a pastebin like Gist.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;File transfers&lt;/strong&gt;: Slack does them. IRC also does them through XDCC, but this can be difficult to get working.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Persistent sessions&lt;/strong&gt;: Slack makes it so that you can see what you missed when you return. With IRC, you don’t have this. If you want it, you can set up an IRC bouncer like &lt;a href=&quot;http://znc.in/&quot; target=&quot;_blank&quot;&gt;ZNC&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Integrations&lt;/strong&gt;: with things like build bots. This was never actually a problem with IRC. IRC has always been significantly better at this than Slack. There is &lt;em&gt;definitely&lt;/em&gt; an IRC client library for your favorite programming language, and you can write your own client from scratch in a matter of minutes anyway. There’s an &lt;a href=&quot;https://github.com/nandub/hubot-irc&quot; target=&quot;_blank&quot;&gt;IRC&lt;/a&gt; backend for Hubot, too. GitHub has a built-in hook for announcing repository activity in an IRC channel.&lt;/p&gt;&lt;h2&gt;Other projects are using IRC&lt;/h2&gt;&lt;p&gt;Here’s a short, incomplete list of important FOSS projects using IRC:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Debian&lt;/li&gt;&lt;li&gt;Docker&lt;/li&gt;&lt;li&gt;Django&lt;/li&gt;&lt;li&gt;jQuery&lt;/li&gt;&lt;li&gt;Angular&lt;/li&gt;&lt;li&gt;ReactJS&lt;/li&gt;&lt;li&gt;NeoVim&lt;/li&gt;&lt;li&gt;Node.js&lt;/li&gt;&lt;li&gt;everyone else&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The list goes on for a while. Just fill in another few hundred bullet points with your imagination. Seriously, just join &lt;code&gt;#&amp;lt;project-name&amp;gt;&lt;/code&gt; on Freenode. It probably exists.&lt;/p&gt;&lt;h2&gt;IRC is better for your company, too&lt;/h2&gt;&lt;p&gt;We use IRC at &lt;a href=&quot;https://www.linode.com/&quot; target=&quot;_blank&quot;&gt;Linode&lt;/a&gt;, even for non-technical people. It works great. If you want to reduce the barrier to entry for non-technicals, set up something like &lt;a href=&quot;https://github.com/erming/shout&quot; target=&quot;_blank&quot;&gt;shout&lt;/a&gt; instead. You can also have a pretty no-brainer link to webchat on almost every network, &lt;a href=&quot;http://webchat.esper.net/?nick=&amp;channels=truecraft&quot; target=&quot;_blank&quot;&gt;like this&lt;/a&gt;. If you need file hosting, you can deploy an instance of &lt;a href=&quot;https://github.com/SirCmpwn/sr.ht/&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt; or something like it. You can also host IRC servers on your own infrastructure, which avoids leaving sensitive conversations on someone else’s servers.&lt;/p&gt;&lt;h2&gt;Please use IRC&lt;/h2&gt;&lt;p&gt;In short, I’d really appreciate it if we all quit using Slack like this. It’s not appropriate for FOSS projects. I would much rather join your channel with the client I already have running. That way, I’m more likely to stick around after I get help with whatever issue I came to you for, and contribute back by helping others as I idle in your channel until the end of time. On Slack, I leave as soon as I’m done getting help because tabs in my browser are precious real estate.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=10486541&quot; target=&quot;_blank&quot;&gt;First discussion on Hacker News&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=11013136&quot; target=&quot;_blank&quot;&gt;Second discussion on Hacker News&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Updates&lt;/h2&gt;&lt;p&gt;Addressing feedback on this article.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Slack IRC bridge&lt;/strong&gt;: Slack provides an IRC bridge that lets you connect to Slack with an IRC client. I’ve used it - it’s a bit of a pain in the ass to set up, and once you have it, it’s not ideal. They did put some effort into it, though, and it’s usable. I’m not suggesting that Slack as a product is worse than IRC - I’m just saying that it’s not better than IRC for FOSS projects, and probably not that much better for companies.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Clients&lt;/strong&gt;: Slack has several clients that use the API. That being said, there are fewer of them and for fewer platforms than IRC clients, and there are more libraries around IRC than there are for Slack. Also, the bigger issue is that I already have an IRC client, which I use for the hundreds of FOSS projects that use IRC, and I don’t want to add a Slack client for one or two projects.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Gitter&lt;/strong&gt;: Gitter is bad for many of the same reasons Slack is. Please don’t use it over IRC.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ircv3&lt;/strong&gt;: Check it out: &lt;a href=&quot;http://ircv3.net&quot; target=&quot;_blank&quot;&gt;ircv3.net&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;irccloud&lt;/strong&gt;: Is really cool and solves all of the problems. &lt;a href=&quot;https://www.irccloud.com/&quot; target=&quot;_blank&quot;&gt;irccloud.com&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2018-03-12&lt;/strong&gt;: Slack is shutting down the IRC and XMPP gateways.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Please-stop-using-slack/</link>
        
        <pubDate>Sun, 01 Nov 2015 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Please-stop-using-slack/</guid>
      </item>
    
      <item>
        
        
          <title>A practical understanding of Flux</title>
          <description>
            &lt;p&gt;&lt;a href=&quot;https://facebook.github.io/react/&quot; target=&quot;_blank&quot;&gt;React.js&lt;/a&gt; and the &lt;a href=&quot;https://facebook.github.io/flux/&quot; target=&quot;_blank&quot;&gt;Flux&lt;/a&gt; are shaping up to be some of the most important tools for web development in the coming years. The MVC model was strong on the server when we decided to take the frontend seriously, and it was shoehorned into the frontend since we didn’t know any better. React and Flux challenge that and I like where it’s going very much. That being said, it was very difficult for me to get into. I put together this blog post to serve as a more &lt;em&gt;practical&lt;/em&gt; guide - the upstream documentation tells you a lot of concepts and expects you to put them together yourself. Hopefully at the end of this blog post you can confidently start writing things with React+Flux instead of reading brain-melting docs for a few hours like I did.&lt;/p&gt;&lt;p&gt;At the core of it, React and Flux are very simple and elegant. Far more simple than the voodoo sales pitch upstream would have you believe. To be clear, &lt;strong&gt;React&lt;/strong&gt; is a framework-ish that lets you describe your UI through reusable components, and includes &lt;em&gt;jsx&lt;/em&gt; for describing HTML elements directly in your JavaScript code. &lt;strong&gt;Flux&lt;/strong&gt; is an &lt;em&gt;optional&lt;/em&gt; architectural design philosophy that you can adopt to help structure your applications. I have been using &lt;a href=&quot;https://babeljs.io/&quot; target=&quot;_blank&quot;&gt;Babel&lt;/a&gt; to compile my React+Flux work, which gives me ES6/ES7 support - I strongly suggest you do the same. This blog post assumes you’re doing so. For a crash course on ES6, &lt;a href=&quot;http://git.io/es6features&quot; target=&quot;_blank&quot;&gt;read this entire page&lt;/a&gt;. Crash course for ES7 is omitted here for brevity, but &lt;a href=&quot;https://gist.github.com/SirCmpwn/2e8e455c91494b7c3713&quot; target=&quot;_blank&quot;&gt;click this&lt;/a&gt; if you’re interested.&lt;/p&gt;&lt;h2&gt;Flux overview&lt;/h2&gt;&lt;p&gt;Flux is based on a unidirectional data flow. The direction is: dispatcher ➜ stores ➜ views, and the data is actions. At the stores or views level, you can give actions to the dispatcher, which passes them down the line.&lt;/p&gt;&lt;p&gt;Let’s explain exactly what piece is, and how it fits in to your application. After this I’ll tell you some specific details and I have a starter kit prepared for you to grab as well.&lt;/p&gt;&lt;h3&gt;Dispatcher&lt;/h3&gt;&lt;p&gt;The dispatcher is very simple. Anything can register to receive a callback when an “action” happens. There is one dispatcher and one set of callbacks, and everything that registers for it will receive every action given to the dispatcher, and can do with this as it pleases. Generally speaking you will only have the stores listen to this. The kind of actions you will send along may look something like this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Add a record&lt;/li&gt;&lt;li&gt;Delete a record&lt;/li&gt;&lt;li&gt;Fetch a record with a given ID&lt;/li&gt;&lt;li&gt;Refresh a store&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Anything that would change data is going to be given to the dispatcher and passed along to the actions. Since everything receives every action you give to the dispatcher, you have to encode something into each action that describes what it’s for. I use objects that look something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;json&quot;&gt;{
    &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;action&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;STORE_NAME.ACTION_TYPE.ETC&amp;quot;&lt;/span&gt;,
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;...&lt;/code&gt; is whatever extra data you need to include (the ID of the record to fetch, the contents of the record to be added, the property that needs to change, etc). Here’s an example payload:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;json&quot;&gt;{
    &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;action&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;ACCOUNTS.CREATE.USER&amp;quot;&lt;/span&gt;,
    &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;SirCmpwn&amp;quot;&lt;/span&gt;,
    &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;drew@ddevault.org&amp;quot;&lt;/span&gt;,
    &lt;span class=&quot;string_special_key string&quot;&gt;&amp;quot;password&amp;quot;&lt;/span&gt;: &lt;span class=&quot;string&quot;&gt;&amp;quot;hunter2&amp;quot;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Accounts store is listening for actions that start with &lt;code&gt;ACCOUNTS.&lt;/code&gt; and when it sees &lt;code&gt;CREATE.USER&lt;/code&gt;, it knows a new user needs to be created with these details.&lt;/p&gt;&lt;h3&gt;Stores&lt;/h3&gt;&lt;p&gt;The stores just have ownership of data and handle any changes that happen to that data. When the data changes, they raise events that the views can subscribe to to let them know what’s up. There’s nothing magic going on here (I initially thought there was magic). Here’s a really simple store:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;Dispatcher&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;whatever&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;UserStore&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;property function_method&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;_users&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;Dispatcher&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;property function_method&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;_users&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;property function_method&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;ACCOUNTS.CREATE.USER&amp;quot;&lt;/span&gt;:
            &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;_users&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt; 
                &lt;span class=&quot;string&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt;: &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;string&quot;&gt;&amp;quot;email&amp;quot;&lt;/span&gt;: &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;string&quot;&gt;&amp;quot;password&amp;quot;&lt;/span&gt;: &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;password&lt;/span&gt;
            &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;raiseChangeEvent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// Exercise for the reader&lt;/span&gt;
            &lt;span class=&quot;keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;UserStore&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;UserStore&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yeah, that’s all there is to it. Each store should be a singleton. You use it like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;UserStore&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;whatever/UserStore&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;UserStore&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;UserStore&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;registerChangeEvent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;UserStore&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// This has changed now&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stores end up having a lot of boilerplate. I haven’t quite figured out the best way to address that yet.&lt;/p&gt;&lt;h3&gt;Views&lt;/h3&gt;&lt;p&gt;Views are react components. What makes React components interesting is that they re-render the whole thing when you call &lt;code&gt;setState&lt;/code&gt;. If you want to change the way it appears on the page for any reason, a call to &lt;code&gt;setState&lt;/code&gt; will need to happen. And here are the two circumstances under which they will change:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;In response to user input to change non-semantic view state&lt;/li&gt;&lt;li&gt;In response to a change event from a store&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The first bullet here means that you can call &lt;code&gt;setState&lt;/code&gt; to change view states, but not data. The second bullet is for when the data changes. When you change view states, this refers to things like “click button to reveal form”. When you change data, this refers to things like “a new record was created, show it”, or even “a single property of a record changed, show that change”.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Wrong way&lt;/strong&gt;: you have a text box that updates the “name” of a record. When the user presses the “Apply” key, the view will re-render itself with the new name.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Right way&lt;/strong&gt;: When you press “Apply”, the view sends an action to the dispatcher to apply the change. The relevant store picks up the action, applies the change to its own data store, and raises an event. Your view hears that event and re-renders itself.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://facebook.github.io/flux/img/flux-simple-f8-diagram-1300w.png&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://facebook.github.io/flux/img/flux-simple-f8-diagram-with-client-action-1300w.png&quot;&gt;&lt;/p&gt;&lt;h3&gt;Why bother?&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Easy to have stores depend on each other&lt;/li&gt;&lt;li&gt;All views that depend on the same stores are updated when it changes&lt;/li&gt;&lt;li&gt;It follows that all cross-store dependencies are updated in a similar fashion&lt;/li&gt;&lt;li&gt;Single source of truth for data&lt;/li&gt;&lt;li&gt;Easy as pie to pick up and maintain with little knowledge of the codebase&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Practical problems&lt;/h2&gt;&lt;p&gt;Here are some problems I ran into, and the fluxy solution to each.&lt;/p&gt;&lt;h3&gt;Need to load data async&lt;/h3&gt;&lt;p&gt;You have a list of DNS records to show the user, but they’re hanging out on the server instead of in JavaScript objects. Here’s how you accomodate for this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;When you use a store, call &lt;code&gt;Store.fetchIfNecessary()&lt;/code&gt; first.&lt;/li&gt;&lt;li&gt;When you pull data from the store, expect &lt;code&gt;null&lt;/code&gt; and handle this elegantly.&lt;/li&gt;&lt;li&gt;When the initial fetch finishes in the store, raise a change event.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;From &lt;code&gt;fetchIfNecessary&lt;/code&gt; in the store, go do the request unless it’s in progress or done. On the view side, show a loading spinner or something if you get &lt;code&gt;null&lt;/code&gt;. When the change event happens, whatever code set the state of your component initially will be re-run, and this time it won’t get &lt;code&gt;null&lt;/code&gt; - deal with it appropriately (show the actual UI).&lt;/p&gt;&lt;p&gt;This works for more than things that are well-defined at dev time. If you need to, for example, fetch data for an arbitrary ID:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;View calls &lt;code&gt;Store.userById(10)&lt;/code&gt; and gets &lt;code&gt;null&lt;/code&gt;, renders lack of data appropriately&lt;/li&gt;&lt;li&gt;Store is like “my bad” and fetches it from the server&lt;/li&gt;&lt;li&gt;Store raises change event when it arrives and the view re-renders&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Batteries not included&lt;/h3&gt;&lt;p&gt;Upstream, in terms of actual usable code, flux just gives you a dispatcher. You also need something to handle your events. This is easy to roll yourself, or you can grab one of a bazillion things online that will do it for you. There is also no base Store class for you, so make one of those. You should probably just include some shared code for raising events and consuming actions. Mine looks something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;UserStore&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;Store&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;property function_method&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;variable_builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;USER&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;_users&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;variable_builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;CREATE.USER&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;userCreated&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;property function_method&quot;&gt;userCreated&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;_users&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;...&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;variable_builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;raiseChangeEvent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;property&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;property&quot;&gt;Users&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;_users&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do what works best for you.&lt;/p&gt;&lt;h2&gt;Starter Kit&lt;/h2&gt;&lt;p&gt;If you want something with the batteries in and a base to build from, I’ve got you covered. Head over to &lt;a href=&quot;https://github.com/SirCmpwn/react-starter-kit&quot; target=&quot;_blank&quot;&gt;SirCmpwn/react-starter-kit&lt;/a&gt; on Github.&lt;/p&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;React and Flux are going to be big. This feels like the right way to build a frontend. Hopefully I saved you from all the headache I went through trying to “get” this stuff, and I hope it serves you well in the future. I’m going to be pushing pretty hard for this model at my new gig, so I may be writing more blog posts as I explore it in a large-scale application - stay tuned.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/A-practical-understanding-of-Flux/</link>
        
        <pubDate>Mon, 20 Jul 2015 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/A-practical-understanding-of-Flux/</guid>
      </item>
    
      <item>
        
        
          <title>Hooks - running stuff on Github hooks</title>
          <description>
            &lt;p&gt;I found myself in need of a simple tool for deploying a project on every git commit, but I didn’t have a build server set up. This led to Hooks - a very simple tool that allows you to run arbitrary commands when Github’s hooks execute.&lt;/p&gt;&lt;p&gt;The configuration is very simple. In &lt;code&gt;/etc/hooks.conf&lt;/code&gt;, write:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[truecraft]
repository=SirCmpwn/TrueCraft
branch=master
command=systemctl restart hooks
valid_ips=204.232.175.64/27,192.30.252.0/22,127.0.0.1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You may include any number of hooks. The &lt;code&gt;valid_ips&lt;/code&gt; entry in that example allows you to accept hooks from Github and from localhost. Then you run Hooks itself, it will execute your command when you push a commit to your repository.&lt;/p&gt;&lt;p&gt;This allows you to do continuous deployment on the cheap and easy. I hope you find it useful. &lt;a href=&quot;https://github.com/SirCmpwn/hooks&quot; target=&quot;_blank&quot;&gt;Hooks&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Hooks/</link>
        
        <pubDate>Sun, 19 Apr 2015 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Hooks/</guid>
      </item>
    
      <item>
        
        
          <title>On the profitability of image hosting websites</title>
          <description>
            &lt;p&gt;I’ve been doing a lot of thought about whether or not it’s even possible to both run a simple website &lt;em&gt;and&lt;/em&gt; turn a profit from it &lt;em&gt;and&lt;/em&gt; maintain a high quality of service. In particular, I’m thinking about image hosts, considering that I run one (a rather unprofitable one, too), but I would think that my thoughts on this matter apply to more kinds of websites. That being said, I’ll just talk about media hosting because that’s where I have tangible expertise.&lt;/p&gt;&lt;p&gt;I think that all image hosts suffer from the same sad pattern of eventual failure. That pattern is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a great image hosting website (you should stop here)&lt;/li&gt;&lt;li&gt;Decide to monetize it&lt;/li&gt;&lt;li&gt;Add advertising&lt;/li&gt;&lt;li&gt;Stop allowing hotlinking&lt;/li&gt;&lt;li&gt;Add more advertising&lt;/li&gt;&lt;li&gt;Add social tools like comments, voting - attempt build a community to look at your ads&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Monetization is a poison. You start realizing that you wrote a shitty website in PHP on shared hosting and it can’t handle the traffic. You spend more money on it and realize you don’t like spending your money on it, so you decide to monetize, and now the poison has got you. There’s an extremely fine line to walk with monetization. You start wanting to make enough money to support your servers, but then you think to yourself “well, I worked hard for this, maybe I should make a living from it!” This introduces several problems.&lt;/p&gt;&lt;p&gt;First of all, you made an image hosting website. It’s already perfect. Almost anything you can think of adding will only make it worse. If you suddenly decide that you need to spend more time on it to justify taking money from it, then you have a lot of time to get things wrong. You eventually run out of the good features and start implementing the bad ones.&lt;/p&gt;&lt;p&gt;More importantly, though, you realize that you should be making &lt;em&gt;more&lt;/em&gt; money. Maybe you can turn this into a nice job working on your own website! And that means you should start a business and assign yourself a salary and start making a profit and hire new people. The money has to come from somewhere. So you make even more compromises. Eventually, people stop using your service. People start to &lt;em&gt;detest&lt;/em&gt; your service. It can get so bad that people will refuse to click on any link that leads to your website. Your users will be harassed for continuing to use your site. &lt;strong&gt;You fail, and everyone hates you.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;This trend is observable with PhotoBucket, ImageShack, TinyPic, the list goes on. The conclusion I’ve drawn from this is that &lt;strong&gt;it is impossible to run a profitable image hosting service without sacrificing what makes your service worthwhile&lt;/strong&gt;. We have arrived at a troubling place with the case of Imgur, however.  MrGrim (the creator of Imgur) also identified this trend and decided to put a stop to it by building a simple image hosting service for Reddit. It had great intentions, check out the old archive.org mirror of it&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.  With these great intentions and a great service, Imgur rose to become the 46th most popular website globally&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fn-2-ref-1&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and 18th in the United States alone, on the shoulders of Reddit, which now ranks 47th. I’m going to expand upon this here, particularly with respect to Reddit, but I included the ranks here to dissuade anyone who says “there’s more than Reddit out there” in response to this post. Reddit is a &lt;em&gt;huge&lt;/em&gt; deal.&lt;/p&gt;&lt;p&gt;Other image hosts died down when people recognized their problems. Imgur has reached a critical mass where that will not happen. 20% of all new Reddit posts are Imgur, and most users just don’t know better than to use anything else. That being said, Imgur shows the signs of the image hosting poison. They stopped being an image hosting website and became their own community. They added advertising, which is fine on its own, but then they started redirecting direct links&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fn-3-ref-1&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; to pages with ads. And still, their userbase is just as strong, despite better alternatives appearing.&lt;/p&gt;&lt;p&gt;I’m not sure what to do about Imgur. I don’t like that they’ve won the mindshare with a staggering margin. I do know that I’ve tried to make my own service immune to the image hosting poison. We run it incredibly lean - we handle over 10 million HTTP requests per day on a single server that also does transcoding and storage for $200 per month. We get about $20-$30 in monthly revenue from our Project Wonderful&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fn-4-ref-1&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; ads, and a handful of donations that usually amount to less than $20. Fortunately, $150ish isn’t a hard number to pay out of our own pockets every month, and we’ve made a damn good website that’s extremely scalable to keep our costs low. We haven’t taken seed money, and we’re not really the sort to fix problems by throwing more money at it. We also won’t be hiring any paid staff any time soon, so our costs are pretty much constant. On top of that, if we do fall victim to the image hosting poison, 100% of our code is open source, so the next service can skip R&amp;D and start being awesome immediately. Even with all of that, though, all I can think of doing is sticking around until people realize that Imgur really does suck.&lt;/p&gt;&lt;p&gt;&lt;em&gt;2017-03-07 update&lt;/em&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;mediacru.sh shut down (out of money)&lt;/li&gt;&lt;li&gt;pomf.se shut down (out of money)&lt;/li&gt;&lt;li&gt;minus.com shut down after going down the decline described in this post&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I have started a private service called &lt;a href=&quot;https://sr.ht&quot; target=&quot;_blank&quot;&gt;sr.ht&lt;/a&gt;, which I aim to use to fix the problem by only letting my friends and I use it. It has controlled growth and won’t get too big and too expensive. It’s on Github if you want to &lt;a href=&quot;https://github.com/SirCmpwn/sr.ht&quot; target=&quot;_blank&quot;&gt;use it&lt;/a&gt;.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-profitability-of-online-services/</link>
        
        <pubDate>Fri, 10 Oct 2014 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-profitability-of-online-services/</guid>
      </item>
    
      <item>
        
        
          <title>Process scheduling and multitasking in KnightOS</title>
          <description>
            &lt;p&gt;I’m going to do some blogging about technical decisions made with &lt;a href=&quot;http://knightos.org&quot; target=&quot;_blank&quot;&gt;KnightOS&lt;/a&gt;. It’s an open-source project I’ve been working on for the past four years to build an open-source Unix-like kernel for TI calculators (in assembly). It’s been a cool platform on top of which I can research low level systems concepts and I thought I’d share some of my findings with the world.&lt;/p&gt;&lt;p&gt;So, first of all, what is scheduling? For those who are completely out of the loop, I’ll explain what exactly it is and why it’s neccessary. Computers run on a CPU, which executes a series of instructions in order. Each core is not capable of running several instructions concurrently. However, you can run hundreds of processes at once on your computer (and you probably are doing so as you read this article). There are a number of ways of accomplishing, but the one that suits the most situations is &lt;em&gt;preemtive multitasking&lt;/em&gt;. This is what KnightOS uses. You see, a CPU can only execute one instruction after another, but you can “raise an interrupt”. This will halt execution and move to some other bit of code for a moment. This can be used to handle various events (for example, the GameBoy raises an interrupt when a button is pressed). One of these events is often a timer, which raises an interrupt at a fixed interval. This is the mechanism by which preemptive multitasking is accomplished.&lt;/p&gt;&lt;p&gt;Let’s say for a moment that you have two programs loaded into memory and running, at addresses 0x1000 and 0x2000. Your kernel has an interrupt handler at 0x100. So if program A is running and an interrupt fires, the following happens:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;0x1000 is pushed to the stack as the return address&lt;/li&gt;&lt;li&gt;The program counter is set to 0x100 and the interrupt runs&lt;/li&gt;&lt;li&gt;The interrupt concludes and returns, which pops 0x1000 from the stack and into the program counter.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Once the interrput handler runs, however, the kernel has a chance to be sneaky:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;0x1000 is pushed to the stack as the return address&lt;/li&gt;&lt;li&gt;The program counter is set to 0x100 and the interrupt runs&lt;/li&gt;&lt;li&gt;The interrupt removes 0x1000 from the stack and puts 0x2000 there instead&lt;/li&gt;&lt;li&gt;The interrupt concludes and returns, which pops 0x2000 from the stack and into the program counter.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Now the interrupt has switched the CPU from program A to program B. And the next time an interrupt occurs, the kernel can switch from program B to program A. This event is called a “context switch”.  This is the basis of preemptive multitasking. On top of this, however, there are lots of ideas around which processes should get CPU time and when. Some systems have more complex schedulers, but KnightOS runs on limited hardware and I wanted the context switch to be short and sweet so that the running processes get as much of the CPU as possible. I’ll explain the simple KnightOS scheduling algorithm here. First, its goals:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Short and simple context switches&lt;/li&gt;&lt;li&gt;Ability to suspend processes when not in foreground&lt;/li&gt;&lt;li&gt;Ability to run background processes&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What KnightOS uses is a simple round robin with the ability to suspend threads. That is, we have a list of processes and then some flags, among which is whether or not the processes is currently suspended. So say we have this list of processes in memory:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;1: PC=0x2000, not suspended&lt;/li&gt;&lt;li&gt;2: PC=0x2000, not suspended&lt;/li&gt;&lt;li&gt;3: PC=0x2000, suspended&lt;/li&gt;&lt;li&gt;4: PC=0x2000, not suspended&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;As process 1 is running and an interrupt fires, the kernel looks at this table and picks the next non-suspended process to run - process 2. On the next interrupt, it does it again, skipping process 3 and giving time to process 4.&lt;/p&gt;&lt;p&gt;To actually implement this, we have to think about the stack. KnightOS runs on z80 processors, which have a single stack and a shared memory space. The CPU uses the PC register to keep track of which address the current instruction is at. That is, say you compile this code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ld a, 10
inc a
ld (hl), a
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This compiles to the machine code 3E 0A 3C 77. Say we load this program at 0x8000 - then 0x8000 will point to &lt;code&gt;ld a, 10&lt;/code&gt;. When the CPU finishes executing this instruction, it advances PC to 0x8002 (since &lt;code&gt;ld a, 10&lt;/code&gt; is a two-byte instruction). The next instruction it executes will be &lt;code&gt;inc a&lt;/code&gt;, and then PC advances to 0x8003.&lt;/p&gt;&lt;p&gt;The stack is used for a lot of things. It can be used to save values, and it is used to call subroutines. It is also used for interrupts. It’s like the same stacks you use in higher level applications, but it’s at a very low level. When an interrupt fires, the current value of PC is pushed to the stack. Then PC is set to the interrupt routine, and then when that’s done the top of the stack is removed and placed into PC (effectively returning control to the original location). However, since the stack is used for much more than that, we have additional things to consider.&lt;/p&gt;&lt;p&gt;In KnightOS, when a new process starts, it’s allocated a stack in memory and the CPU’s stack pointer (SP) is set to its address. When an interrupt happens, we need to change the stack to point at some other process so it has time to run (since that’s where its PC is). However, we need to make sure that the first processes stack is left intact. Since we allocate a new stack for the next process, we can simply change SP to that processes stack. This will leave behind the value of PC that was pushed during the interrupt for the previous process, and lo and behlod a similar value of PC is waiting on top of the other processes stack.&lt;/p&gt;&lt;p&gt;So that’s it! We do a simple round robin, skipping suspended processes and following the procedure outlined above to switch between them. This is how KnightOS shares one CPU with several “concurrent” processes. Operating systems like Linux use more complicated schedulers with more interesting theory if you’d like some additional reading. And of course, since KnightOS is open source, you may enjoy reading all of our code for handling this stuff (in assembly):&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/KnightOS/kernel/blob/master/src/00/interrupt.asm&quot; target=&quot;_blank&quot;&gt;Context switching&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/KnightOS/kernel/blob/master/src/00/thread.asm#L72&quot; target=&quot;_blank&quot;&gt;Stack allocation during process creation&lt;/a&gt;&lt;/p&gt;&lt;p&gt;We’re hanging out on #knightos on Freenode if you want to chat about cool low-level stuff like scheduling and memory management.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Process-scheduling-in-KnightOS/</link>
        
        <pubDate>Tue, 02 Sep 2014 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Process-scheduling-in-KnightOS/</guid>
      </item>
    
      <item>
        
        
          <title>Python&apos;s datetime sucks</title>
          <description>
            &lt;p&gt;I’ve been playing with Python for about a year now, and I like pretty much everything about it. There’s one thing that’s really rather bad and really should not be that bad, however - date &amp; time support. It’s ridiculous how bad it is in Python. This is what you get with the standard datetime module:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The current time and strftime, with a reasonable set of properties&lt;/li&gt;&lt;li&gt;Time deltas with days, seconds, and microseconds and nothing else&lt;/li&gt;&lt;li&gt;Acceptable support for parsing dates and times&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What you don’t get is:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Meaningful time deltas&lt;/li&gt;&lt;li&gt;Useful arithmetic&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Date and time support is a rather tricky thing to do and it’s something that the standard library should support well enough to put it in the back of your mind instead of making you do all the work.&lt;/p&gt;&lt;p&gt;We’ll be comparing it to C# and .NET.&lt;/p&gt;&lt;p&gt;Let’s say I want to get the total hours between two &lt;code&gt;datetime&lt;/code&gt;s.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;cs&quot;&gt;&lt;span class=&quot;comment&quot;&gt;// C#&lt;/span&gt;
&lt;span class=&quot;type variable&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;type_builtin&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;hours&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;TotalHours&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# Python&lt;/span&gt;
&lt;span class=&quot;constructor constant variable&quot;&gt;a&lt;/span&gt;, &lt;span class=&quot;constructor constant variable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; ...
&lt;span class=&quot;constructor constant variable&quot;&gt;hours&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;constructor constant variable&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;a&lt;/span&gt;).&lt;span class=&quot;constructor property constant variable&quot;&gt;seconds&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s not so bad. How about getting the time exactly one month in the future:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;cs&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable function&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;constructor constant variable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable&quot;&gt;date&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;now&lt;/span&gt;() &lt;span class=&quot;operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;timedelta&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;30&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well, that’s not ideal. In C#, if you add one month to Janurary 30th, you get Feburary 28th (or leap day if appropriate). In Python, you could write a janky function to do this for you, or you could use the crappy alternative I wrote above.&lt;/p&gt;&lt;p&gt;How about if I want to take a delta between dates and show it somewhere, like a countdown? Say an event is happening at some point in the future and I want to print “3 days, 5 hours, 12 minutes, 10 seconds left”. This is distinct from the first example, which could give you “50 hours”, whereas this example would give you “2 days, 2 hours”.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;cs&quot;&gt;&lt;span class=&quot;type variable&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;future&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; ..&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;delta&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;future&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;variable&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;{0} days, {1} hours, {2} minutes, {3} seconds left&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;variable&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;Days&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;variable&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;Hours&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;variable&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;Minutes&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;variable&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;Seconds&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# ...mess of math you have to implement yourself omitted...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maybe I have a website where users can set their locale?&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;cs&quot;&gt;&lt;span class=&quot;type variable&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; ..&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;variable&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;quot;some format string&amp;quot;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;variable&quot;&gt;Locale&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;constructor constant variable&quot;&gt;locale&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;setlocale&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;locale&lt;/span&gt;.&lt;span class=&quot;constructor property constant variable&quot;&gt;LC_TIME&lt;/span&gt;, &lt;span class=&quot;string&quot;&gt;&amp;quot;sv_SE&amp;quot;&lt;/span&gt;) &lt;span class=&quot;comment&quot;&gt;# Global!&lt;/span&gt;
&lt;span class=&quot;constructor function_builtin constant variable function&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;constructor constant variable&quot;&gt;time&lt;/span&gt;.&lt;span class=&quot;constructor property constant function_method variable&quot;&gt;strftime&lt;/span&gt;(&lt;span class=&quot;string&quot;&gt;&amp;quot;some format string&amp;quot;&lt;/span&gt;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By the way, that Python one doesn’t work on Windows. It uses system locales names which are different on Windows than on Linux or OS X. Mono (cross-platform .NET) handles this for you on any system.&lt;/p&gt;&lt;p&gt;And a few other cases that are easy in .NET and not in Python:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Days since the start of this year&lt;/li&gt;&lt;li&gt;Constants like the days in every month&lt;/li&gt;&lt;li&gt;Is it currently DST in this timezone?&lt;/li&gt;&lt;li&gt;Is this a leap year?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In short, Python’s datetime module could really use a lot of fleshing out. This is common stuff and easy for a naive programmer to do wrong.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Python-datetime-sucks/</link>
        
        <pubDate>Sat, 28 Jun 2014 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Python-datetime-sucks/</guid>
      </item>
    
      <item>
        
        
          <title>Go&apos;s error handling doesn&apos;t sit right with me</title>
          <description>
            &lt;p&gt;I’ll open up by saying that I am not a language designer, and I do like a lot of things about Go. I just recently figured out how to describe why Go’s error handling mechanics don’t sit right with me.&lt;/p&gt;&lt;p&gt;If you aren’t familiar with Go, here’s an example of how Go programmers might do error handling:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;go&quot;&gt;&lt;span class=&quot;variable&quot;&gt;result&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;SomethingThatMightGoWrong&lt;/span&gt;()
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
    &lt;span class=&quot;comment&quot;&gt;// Handle error&lt;/span&gt;
}
&lt;span class=&quot;comment&quot;&gt;// Proceed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s extrapolate this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;go&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;variable function&quot;&gt;MightFail&lt;/span&gt;() {
    &lt;span class=&quot;variable&quot;&gt;result&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;doStuffA&lt;/span&gt;()
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
        &lt;span class=&quot;comment&quot;&gt;// Error handling omitted&lt;/span&gt;
    }
    &lt;span class=&quot;variable&quot;&gt;result&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;doStuffB&lt;/span&gt;()
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
        &lt;span class=&quot;comment&quot;&gt;// Error handling omitted&lt;/span&gt;
    }
    &lt;span class=&quot;variable&quot;&gt;result&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;doStuffC&lt;/span&gt;()
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
        &lt;span class=&quot;comment&quot;&gt;// Error handling omitted&lt;/span&gt;
    }
    &lt;span class=&quot;variable&quot;&gt;result&lt;/span&gt;, &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;function_builtin variable function&quot;&gt;doStuffD&lt;/span&gt;()
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;nil&lt;/span&gt; {
        &lt;span class=&quot;comment&quot;&gt;// Error handling omitted&lt;/span&gt;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go has good intentions by removing exceptions. They add a lot of overhead and returning errors isn’t a bad thing in general. However, I spend a lot of my time writing assembly. Assembly can use similar mechanics, but I’m spoiled by it (I know, spoiled by assembly?) and I can see how Go could have done better. In assembly, &lt;code&gt;goto&lt;/code&gt; (or instructions like it) are the only means you have of branching. It’s not like other languages where it’s taboo - you pretty much &lt;em&gt;have&lt;/em&gt; to use it. Most assembly also makes it fancy and conditional. For example:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;goto condition, label
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This would jump to &lt;code&gt;label&lt;/code&gt; given that &lt;code&gt;condition&lt;/code&gt; is met. Like Go, assembly generally doesn’t have exceptions or anything similar. In my own personal flavor of assembly, I have my functions return error codes as well.  Here’s how it’s different, though. Let’s look at some code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;call somethingThatMightFail
jp nz, errorHandler
call somethingThatMightFailB
jp nz, errorHandler
call somethingThatMightFailC
jp nz, errorHandler
call somethingThatMightFailD
jp nz, errorHandler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The difference here is that all functions return errors in the same way - by resetting the Z flag. If that flag is set, we do a quick branch (the &lt;code&gt;jp&lt;/code&gt; instruction is short for &lt;code&gt;jump&lt;/code&gt;) to the error handler. It’s not clear from looking at this snippet, but the error code is stored in the A register, which the &lt;code&gt;errorHandler&lt;/code&gt; recognizes as an error code and shows an appropriate message for. We can have one error handler for an entire procedure, and it feels natural.&lt;/p&gt;&lt;p&gt;In Go, you have to put an if statement here. Each error caught costs you three lines of code in the middle of your important logic flow. With languages that throw exceptions, you have all the logic in a readable procedure, and some error handling at the end of it all. With Go, you have to throw a bunch of 3-line-minimum error handlers all over the middle of your procedure.&lt;/p&gt;&lt;p&gt;In my examples, you can still return errors like this, but you can do so with a lot less visual clutter. One line of error handling is better than 3 lines, if you ask me. Also, no one gives a damn how you format assembly code, so if you wanted to do something like this you’d be fine:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;call somethingThatMightFail
  jp nz, errorHandler
call somethingThatMightFailB
  jp nz, errorHandler
call somethingThatMightFailC
  jp nz, errorHandler
call somethingThatMightFailD
  jp nz, errorHandler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;call somethingThatMightFail  \ jp nz, errorHandler
call somethingThatMightFailB \ jp nz, errorHandler
call somethingThatMightFailC \ jp nz, errorHandler
call somethingThatMightFailD \ jp nz, errorHandler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The point is, I think Go’s error handling stuff make your code harder to read and more tedious to write. The basic idea - return errors instead of throwing them - has good intentions. It’s just that how they’ve done it isn’t so great.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Why-Go-error-handling-doesnt-sit-right-with-me/</link>
        
        <pubDate>Sat, 07 Jun 2014 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Why-Go-error-handling-doesnt-sit-right-with-me/</guid>
      </item>
    
      <item>
        
        
          <title>Hacking on your TI calculator</title>
          <description>
            &lt;p&gt;I’ve built the &lt;a href=&quot;https://github.com/KnightOS/kernel&quot; target=&quot;_blank&quot;&gt;KnightOS kernel&lt;/a&gt;, an open-source OS that runs on several TI calculator models, including the popular TI-83+ family, and recently the new TI-84+ Color Silver Edition. I have published some information on how to build your own operating sytsems for these devices, but I’ve learned a lot since then and I’m writing this blog post to include the lessons I’ve learned from other attempts.&lt;/p&gt;&lt;h2&gt;Prerequisites&lt;/h2&gt;&lt;p&gt;Coming into this, you should be comforable with z80 assembly. It’s possible to write an OS for these devices in C (and perhaps other high-level languages), but proficiency in z80 assembly is still required. Additionally, I don’t consider C a viable choice for osdev on these devices when you consider that the available compliers do not optimize the result very well, and these devices have very limited resources.&lt;/p&gt;&lt;p&gt;You will also have to be comfortable (though not neccessarily expert-level) with these tools:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;make&lt;/li&gt;&lt;li&gt;The assembler of your choice&lt;/li&gt;&lt;li&gt;The toolchain of your choice&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I’m going to gear this post from the perspective of a Linux user, but Windows users should be able to do fine with cygwin. If you’re looking for a good assembler, I suggest &lt;a href=&quot;https://github.com/KnightOS/sass&quot; target=&quot;_blank&quot;&gt;sass&lt;/a&gt;, the assembler KnightOS uses. I built it myself to address the needs of the kernel, and it includes several nice features that make it easier to maintain such a large and complex codebase. Other good choices include &lt;a href=&quot;https://wabbit.codeplex.com/releases/view/45088&quot; target=&quot;_blank&quot;&gt;spasm&lt;/a&gt; and &lt;a href=&quot;https://code.google.com/p/brass-assembler/&quot; target=&quot;_blank&quot;&gt;brass&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For your toolchain, there are a few options, but I’ve built custom tools that work well for KnightOS and should fit into your project as well. You need to accomplish a couple of tasks:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/KnightOS/MakeROM&quot; target=&quot;_blank&quot;&gt;Create ROM files from assembler output&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/KnightOS/CreateUpgrade&quot; target=&quot;_blank&quot;&gt;Create OS upgrades from ROM files&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You also need the &lt;a href=&quot;http://brandonw.net/calculators/keys/&quot; target=&quot;_blank&quot;&gt;cryptographic signing keys&lt;/a&gt; for any of the calculators you intend to support. There are ways to get around using these (which you’ll need to research for the TI-84+ CSE, for example) that you may want to look into. These keys will allow you to add a cryptographic signature on your OS upgrades that will make your calculator think it’s an official Texas Instruments operating system, and you will be able to send it to the device. The CreateUpgrade tool linked above produces signed upgrade files for you, but if you choose to use other tools you may need to find a seperate signing tool.&lt;/p&gt;&lt;p&gt;Additonally, if you target devices with a newer boot code, you’ll have to reflash your boot code or use a tool like &lt;a href=&quot;http://brandonw.net/calcstuff/uosrecv.zip&quot; target=&quot;_blank&quot;&gt;UOSRECV&lt;/a&gt; to send your OS to an actual device.&lt;/p&gt;&lt;h2&gt;What you’re getting into&lt;/h2&gt;&lt;p&gt;You will be replacing everything on the calculator with your own system (though if you want to retain compatability with TIOS like &lt;a href=&quot;http://brandonw.net/calculators/OS2/&quot; target=&quot;_blank&quot;&gt;OS2&lt;/a&gt; tried to, feel free). You’ll need to do &lt;em&gt;everything&lt;/em&gt;, including common things like providing your own multiplication functions, or drawing functions, or anything else. You’ll also be responsible for initializing the calculator and all of the hardware you want to use (such as the LCD or keypad).&lt;/p&gt;&lt;p&gt;That being said, you can take some code from projects like the KnightOS kernel to help you out. The KnightOS kernel is open sourced under the MIT license, which means you’re free to take any code from it and use it in your own project. I also strongly suggest using it as a reference for when you get stuck.&lt;/p&gt;&lt;p&gt;The advantage to taking on this task is that you can leverage the full potential of these devices. What you’re building for is a 6/15 MHz z80 with 32K or more of RAM, plus plenty of Flash and all sorts of fun hardware. You can also build something that frees your device of proprietary code, if that is what you are interested in (though the proprietary boot code would remain - but that’s a story for another day).&lt;/p&gt;&lt;p&gt;If you plan on making a full blown operating systems that can run arbituary programs and handle all sorts of fun things, you’ll want to make sure you have a strong understanding of programming in general, as well as solid algorithmic knowledge and low-level knowledge. If you don’t know how to use pointers or bit math, or don’t fully understand the details of the device, you may want to try again when you do. That being said, I didn’t know a lot when I started KnightOS (as the community was happy to point out), and now I feel much more secure in my skills.&lt;/p&gt;&lt;h2&gt;Building the basic OS&lt;/h2&gt;&lt;p&gt;We’ll build a simple OS here to get you started, including booting the thing up and showing a simple sprite on the screen. First, we’ll create a simple Makefile. This OS will run on the TI-73, TI-83+, TI-83+ SE, TI-84+, TI-84+ SE, and TI-84+ CSE, as well as the French variations on these devices.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://example.org#deadlink&quot; target=&quot;_blank&quot;&gt;Grab this tarball&lt;/a&gt; with the basic OS to get started. It looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;.
├── build
│   ├── CreateUpgrade.exe
│   ├── MakeROM.exe
│   └── sass.exe
├── inc
│   └── platforms.inc
├── Makefile
└── src
    ├── 00
    │   ├── base.asm
    │   ├── boot.asm
    │   ├── display.asm
    │   └── header.asm
    └── boot
        └── base.asm
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you grab this, run &lt;code&gt;make all&lt;/code&gt; and you’ll get a bunch of ROM files in the &lt;code&gt;bin&lt;/code&gt; directory. I’ll explain a little bit about how it works. The important file here is &lt;code&gt;boot.asm&lt;/code&gt;, but I encourage you to read whatever else you feel like - especially the Makefile.&lt;/p&gt;&lt;h3&gt;Miscellaneous Files&lt;/h3&gt;&lt;p&gt;Here is the purpose of each file, save for boot.asm (which gets its own section later):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The makefile is like a script for building the OS. You should probably learn how these work if you don’t already.&lt;/li&gt;&lt;li&gt;Everything in build/ is part of the suggested toolchain.&lt;/li&gt;&lt;li&gt;The inc folder can be #included to, and includes &lt;code&gt;platforms.inc&lt;/code&gt;, which defines a bunch of useful constants for you.&lt;/li&gt;&lt;li&gt;&lt;code&gt;base.asm&lt;/code&gt; is just a bunch of #include statements, for linking without a linker&lt;/li&gt;&lt;li&gt;&lt;code&gt;display.asm&lt;/code&gt; has some useful display code I pulled out of KnightOS&lt;/li&gt;&lt;li&gt;&lt;code&gt;header.asm&lt;/code&gt; contains the OS header and RST list&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;boot.asm&lt;/h3&gt;&lt;p&gt;The real juciy stuff is boot.asm. This file initializes everything and draws a smiley face in the middle of the screen. Here’s what it does (in order):&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Disable interrupts&lt;/li&gt;&lt;li&gt;Set up memory mappings&lt;/li&gt;&lt;li&gt;Create a stack and set SP accordingly&lt;/li&gt;&lt;li&gt;Initialize the LCD (B&amp;W or color)&lt;/li&gt;&lt;li&gt;Draw a smiley face&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I’m sure your OS will probably want to do more interesting things. The KnightOS kernel, for example, adds on top of this a bunch of kernel state initialization, filesystem initialization, and loads up a boot program.&lt;/p&gt;&lt;p&gt;&lt;code&gt;boot.asm&lt;/code&gt; is well-commented and I encourage you to read through it to get an idea of what needs to be done. The most complicated and annoying bit is the color LCD initialization, which is mostly in &lt;code&gt;display.asm&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;I encourage you to spend some time playing with this. Bring in more things and try to build something simple. Remember, you have no bcalls here. You need to build everything yourself.&lt;/p&gt;&lt;h2&gt;Resources&lt;/h2&gt;&lt;p&gt;There are several things you might want to check out. The first and most obvious is &lt;a href=&quot;http://wikiti.brandonw.net/index.php?title=Calculator_Documentation&quot; target=&quot;_blank&quot;&gt;WikiTI&lt;/a&gt;. I don’t use much here except for the documentation on I/O ports, and you’ll find it useful, too.&lt;/p&gt;&lt;p&gt;The rest of the resources here are links to code in the KnightOS kernel.&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://github.com/KnightOS/kernel/blob/master/src/00/interrupt.asm#L19&quot; target=&quot;_blank&quot;&gt;interrupt handler&lt;/a&gt; is a good reference for anyone wanting to work with interrupts to do things like handle the ON button, link activity, or timers. One good use case here (and what KnightOS uses it for) is preemptive multitasking. Note that you might want to use &lt;code&gt;exx&lt;/code&gt; and &lt;code&gt;ex af, af&amp;apos;&lt;/code&gt; instead of pushing all the registers like KnightOS does. Take special note of how we handle USB activity.&lt;/p&gt;&lt;p&gt;You might want to consider offering some sort of color LCD compatabilty mode like KnightOS does. This allows you to treat it like a black &amp; white screen. The relevant code is &lt;a href=&quot;https://github.com/KnightOS/kernel/blob/master/src/00/display-color.asm&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you want to interact with the keyboard, you’ll probably want to reference the KnightOS keyboard code &lt;a href=&quot;https://github.com/KnightOS/kernel/blob/master/src/00/keyboard.asm&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. You might also consider working out an interrupt-based keyboard driver.&lt;/p&gt;&lt;p&gt;If you’d like to manipulate Flash, you need to run most of it from RAM. You will probably want to reference the &lt;a href=&quot;https://github.com/KnightOS/kernel/blob/master/src/00/flash.asm&quot; target=&quot;_blank&quot;&gt;KnightOS Flash driver&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Skipping to the good part&lt;/h2&gt;&lt;p&gt;It’s entirely possible to avoid writing an entire system by yourself. If you want to dive right in and start immediately making something cool, you might consider grabbing the KnightOS kernel. Right off the bat, you’ll get:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A tree-based filesystem&lt;/li&gt;&lt;li&gt;Multitasking and IPC&lt;/li&gt;&lt;li&gt;Memory management&lt;/li&gt;&lt;li&gt;A standard library (math, sorting, etc)&lt;/li&gt;&lt;li&gt;Library support&lt;/li&gt;&lt;li&gt;Hardware drivers for the keyboard, displays, etc&lt;/li&gt;&lt;li&gt;Color and monochrome graphics (and a compatability layer)&lt;/li&gt;&lt;li&gt;A font and text rendering&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.knightos.org/documentation.html&quot; target=&quot;_blank&quot;&gt;Great documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Full support for 9 calculator models&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The kernel is standalone and open-source, and it runs great without the KnightOS userspace. If you’re interested in that, you can get started &lt;a href=&quot;https://github.com/KnightOS/kernel&quot; target=&quot;_blank&quot;&gt;on GitHub&lt;/a&gt;. We’d also love some contributors, if you want to help make the kernel even better.&lt;/p&gt;&lt;h2&gt;Closing thoughts&lt;/h2&gt;&lt;p&gt;I hope to see a few cool OSes come into being in the TI world. It’s unfortunately sparse in that regard. If you run into any problems, feel free to drop by #knightos on irc.freenode.net, where I’m sure myself or someone else can help answer your questions. Good luck!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Hacking-on-your-TI-calculator/</link>
        
        <pubDate>Tue, 25 Feb 2014 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Hacking-on-your-TI-calculator/</guid>
      </item>
    
      <item>
        
        
          <title>The bug that hides from breakpoints</title>
          <description>
            &lt;p&gt;This is the story of the most difficult bug I ever had to solve. See if you can figure it out before the conclusion.&lt;/p&gt;&lt;h3&gt;Background&lt;/h3&gt;&lt;p&gt;For some years now, I’ve worked on a kernel for Texas Instruments calculators called &lt;a href=&quot;https://github.com/KnightOS/kernel&quot; target=&quot;_blank&quot;&gt;KnightOS&lt;/a&gt;. This kernel is written entirely in assembly, and targets the old-school z80 processor from back in 1976. This classic processor was built without any concept of protection rings. It’s an 8-bit processor, with 150-some instructions and (in this application) 32K of RAM and 32K of Flash. This stuff is so old, I ended up writing most of the KnightOS toolchain from scratch rather than try to get archaic assemblers and compilers running on modern systems.&lt;/p&gt;&lt;p&gt;When you’re working in an enviornment like this, there’s no seperation between kernel and userland. All “userspace” programs run as root, and crashing the entire system is a simple task. All the memory my kernel sets aside for the process table, or memory ownership, file handles, stacks, any other executing process - any program can modify this freely. Of course, we have to rely on the userland to play nice, and it usually does. But when there are bugs, they can be a real pain in the ass to hunt down.&lt;/p&gt;&lt;h3&gt;The elusive bug&lt;/h3&gt;&lt;p&gt;The original bug report: &lt;strong&gt;When running the counting demo and switching between applications, the thread list graphics become corrupted.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;I can reproduce this problem, so I settle into my development enviornment and I set a breakpoint near the thread list’s graphical code. I fire up the emulator and repeat the steps… but it doesn’t happen. This happened consistently: &lt;strong&gt;the bug was not reproduceable when a breakpoint was set&lt;/strong&gt;. Keep in mind, I’m running this in a z80 emulator, so the enviornment is supposedly no different. There’s no debugger attached here.&lt;/p&gt;&lt;p&gt;Though this is quite strange, I don’t immediately despair. I try instead setting a “breakpoint” by dropping an infinite loop in the code, instead of a formal breakpoint. I figure that I can halt the program flow manually and open the debugger to inspect the problem. However, the bug wouldn’t be tamed quite so easily. The bug was unreproducable when I had this psuedo-breakpoint in place, too.&lt;/p&gt;&lt;p&gt;At this point, I started to get a little frustrated. How do I debug a problem that disappears when you debug it? I decided to try and find out what caused it after it had taken place, by setting the breakpoint to be hit only after the graphical corruption happened. Here, I gained some ground. I was able to reproduce it, and &lt;em&gt;then&lt;/em&gt; halt the machine, and I could examine memory and such after the bug was given a chance to have its way over the system.&lt;/p&gt;&lt;p&gt;I discovered the reason the graphics were being corrupted. The kernel kept the length of the process table at a fixed address. The thread list, in order to draw the list of active threads, looks to this value to determine how many threads it should draw. Well, when the bug occured, the value was too high! The thread list was drawing threads that did not exist, and the text rendering puked non-ASCII characters all over the display. But why was that value being corrupted?&lt;/p&gt;&lt;p&gt;It was an oddly specific address to change. None of the surrounding memory was touched. Making it even more odd was the very specific conditions this happened under - only when the counting demo was running. I asked myself, “what makes the counting demo unique?” It hit me after a moment of thought. The counting demo existed to demonstrate non-supsendable threads. The kernel would stop executing threads (or “suspend” them) when they lost focus, in an attempt to keep the system’s very limited resources available. The counting demo was marked as non-suspendable, a feature that had been implemented a few months prior. It showed a number on the screen that counted up forever, and the idea was that you could go give some other application focus, come back, and the number would have been counting up while you were away. A background task, if you will.&lt;/p&gt;&lt;p&gt;A more accurate description of the bug emerged: “the length of the kernel process table gets corrupted when launching the thread list when a non-suspendable thread is running”. What followed was hours and hours of crawling through the hundreds of lines of assembly between summoning the thread list, and actually seeing it. I’ll spare you the details, because they are very boring. We’ll pick the story back up at the point where I had isolated the area in which it occured: applib.&lt;/p&gt;&lt;p&gt;The KnightOS userland offered “applib”, a library of common functions applications would need to get the general UX of the system. Among these was the function &lt;code&gt;applibGetKey&lt;/code&gt;, which was a wrapper around the kernel’s &lt;code&gt;getKey&lt;/code&gt; function. The idea was that it would work the same way (return the last key pressed), but for special keys, it would do the appropriate action for you. For example, if you pressed the F5 key, it would suspend the current thread and launch the thread list. This is the mechanism with which most applications transfer control out of their own thread and into the thread list.&lt;/p&gt;&lt;p&gt;Eager that I had found the source of the issue, I placed a breakpoint nearby. That same issue from before struck again - the bug vanished when the breakpoint was set. I tried a more creative approach: instead of using a proper breakpoint, I asked the emulator to halt whenever that address was written to. Even still - the bug hid itself whenever this happened.&lt;/p&gt;&lt;p&gt;I decided to dive into the kernel’s getKey function. Here’s the start of the function, as it appeared at the time:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;getKey:
    call hasKeypadLock
    jr _
    xor a
    ret
_:  push bc
; ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I started going through this code line-by-line, trying to see if there was anything here that could concievably touch the thread table. I noticed a minor error here, and corrected it without thinking:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;getKey:
    call hasKeypadLock
    jr z, _
    xor a
    ret
_:  push bc
; ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The simple error I had corrected: getKey was pressing forward, even when the current thread didn’t have control of the keyboard hardware. This was a silly error - only two characters were omitted.&lt;/p&gt;&lt;p&gt;A moment after I fixed that issue, the answer set in - this was the source of the entire problem. Confirming it, I booted up the emulator with this change applied and the bug was indeed resolved.&lt;/p&gt;&lt;p&gt;Can you guess what happened here? Here’s the other piece of the puzzle to help you out, translated more or less into C for readability:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;applibGetKey&lt;/span&gt;() {
    &lt;span class=&quot;type&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant variable function&quot;&gt;getKey&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;KEY_F5&lt;/span&gt;) {
        &lt;span class=&quot;constant variable function&quot;&gt;launch_threadlist&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;constant variable function&quot;&gt;suspend_thread&lt;/span&gt;()&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
    }
    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;constant variable&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;delimiter&quot;&gt;;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two more details you might not have picked up on:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;applibGetKey is non-blocking&lt;/li&gt;&lt;li&gt;suspend_thread suspends the current thread immediately, so it doesn’t return until the thread resumes.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;The bug, uncovered&lt;/h3&gt;&lt;p&gt;Here’s what actually happened. For most threads (the suspendable kind), that thread stops processing when &lt;code&gt;suspend_thread()&lt;/code&gt; is called. The usually non-blocking applibGetKey function blocks until the thread is resumed in this scenario. However, the counting demo was &lt;em&gt;non-suspendable&lt;/em&gt;. The suspend_thread function has no effect, by design. So, suspend_thread did not block, and the keypress was returned straight away. By this point, the thread list had launched properly and it was given control of the keyboard.&lt;/p&gt;&lt;p&gt;However, the counting demo went back into its main loop, and started calling applibGetKey again. Since the average user’s finger remained pressed against the button for a few moments more, applibGetKey &lt;em&gt;continued to launch the thread list, over and over&lt;/em&gt;. The thread list itself is a special thread, and it doesn’t actually have a user-friendly name. It was designed to ignore itself when it drew the active threads. However, it was &lt;em&gt;not&lt;/em&gt; designed to ignore other instances of itself, the reason being that there would never be two of them running at once. When attempting to draw these other instances, the thread list started rendering text that wasn’t there, causing the corruption.&lt;/p&gt;&lt;p&gt;This bug vanished whenever I set a breakpoint because it would halt the system’s keyboard processing logic. I lifted my finger from the key before allowing it to move on.&lt;/p&gt;&lt;p&gt;The solution was to make the kernel’s getKey function respect hardware locks by fixing that simple, two-character typo. That way, the counting demo, which had no right to know what keys were being pressed, would not know that they key was still being pressed.&lt;/p&gt;&lt;p&gt;The debugging described by this blog post took approximately three weeks.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=7688700&quot; target=&quot;_blank&quot;&gt;Discussion on Hacker News&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/The-worst-bugs/</link>
        
        <pubDate>Sun, 02 Feb 2014 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/The-worst-bugs/</guid>
      </item>
    
      <item>
        
        
          <title>Custom Music Syncing on Android</title>
          <description>
            &lt;p&gt;I have an HTC One, with CyanogenMod installed. I usually use Spotify, but I’ve been wanting to move away from it for a while. The biggest thing keeping me there was the ease of syncing up with my phone - I added music on my PC and it just showed up on my phone.&lt;/p&gt;&lt;p&gt;So, I finally decided to make it work on my phone without Spotify. You might have success if you aren’t using CyanogenMod, but you definitely need to be rooted and you need to access a root shell on your phone. I was using &lt;code&gt;adb shell&lt;/code&gt; to start with, but it has poor terminal emulation. Instead, I ended up installing an SSH daemon on the phone and just using that. Easier to use vim in such an enviornment.&lt;/p&gt;&lt;p&gt;The end result is that a cronjob kicks off each hour on my phone and runs a script that uses rsync to sync up my phone’s music with my desktop’s music. That’s another thing - a prerequisite of this working is that you have to expose your music to the outside world on an SSH server somewhere.&lt;/p&gt;&lt;p&gt;I’ll tell you how I got it working, then you can see if it works for you. It might take some effort on your part to tweak these instructions to fit your requirements.&lt;/p&gt;&lt;h2&gt;Sanity checks&lt;/h2&gt;&lt;p&gt;Get into your phone’s shell and make sure you have basic things installed. You’ll need to make sure you have:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;bash&lt;/li&gt;&lt;li&gt;cron&lt;/li&gt;&lt;li&gt;ssh&lt;/li&gt;&lt;li&gt;rsync&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you don’t have them, you can probably get them by installing busybox.&lt;/p&gt;&lt;h2&gt;Setting up SSH&lt;/h2&gt;&lt;p&gt;We need to generate a key. I tried using ssh-keygen before, but it had problems with rsync on Android. Instead, we use dropbearkey. Generate your key with &lt;code&gt;dropbearkey -t rsa -f /data/.ssh/id_rsa&lt;/code&gt;. You’ll see the public key echoed to stdout. It’s not saved anywhere for you, so grab it out of your shell and put it somewhere - namely, in the authorized_keys file on the SSH server you plan to pull music from.&lt;/p&gt;&lt;p&gt;At this point, you can probably SSH into the server you want to pull from. Run &lt;code&gt;ssh -i /data/.ssh/id_rsa &amp;lt;your server here&amp;gt;&lt;/code&gt; to double check. Note that this isn’t just for fun - you need to do this to get your server into known_hosts, so we can non-interactively access it.&lt;/p&gt;&lt;h2&gt;Making Android more sane&lt;/h2&gt;&lt;p&gt;Now that this is working, we need to clean up a little before cron will run right. Android is only a “Linux” system in the sense that &lt;code&gt;uname&lt;/code&gt; outputs “Linux”. It grossly ignores the FHS and you need to fix it a little. Figure out how to do a nice init.d script on your phone. For my CyanogenMod install, I can add scripts to &lt;code&gt;/data/local/userlocal.d/&lt;/code&gt; and they’ll be run at boot. Here’s my little script for making Android a little more sane:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/system/bin/sh&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# Making /system rw isn&amp;apos;t strictly needed&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;mount&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;remount,rw&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/system&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;mount&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;remount,rw&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/data/var&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/var&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/system/bin&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/bin&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/data/.ssh&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/.ssh&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;crond&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update script and initial import&lt;/h2&gt;&lt;p&gt;The following is the script we’ll use to update your phone’s music library.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/system/xbin/bash&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# Syncs music between a remote computer and this phone&lt;/span&gt;
&lt;span class=&quot;constant property&quot;&gt;RHOST&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;=&amp;lt;remote hostname&amp;gt;
EHOST=&amp;lt;fallback, I use this for connecting from outside my LAN&amp;gt;
RPORT=22
RUSER=&amp;lt;username&amp;gt;
ID=/data/.ssh/id_rsa
RPATH=/path/to/your/remote/music
# Omit the final directory. On my setup, this goes to /sdcard/Music, and my remote is /home/sircmpwn/Music
LPATH=/sdcard

echo &lt;/span&gt;&lt;span class=&quot;embedded constant&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;embedded constant function&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;embedded&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt; /var/log/update-music.log&lt;/span&gt;

&lt;span class=&quot;constant function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-ruvL&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--delete&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--rsh=&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;ssh -p &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;RPORT&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt; -i &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RUSER&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RHOST&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RPATH&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;LPATH&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/update-music-rsync-so.log &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&amp;gt;&amp;amp;1
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; [[ &lt;span class=&quot;operator&quot;&gt;$&lt;/span&gt;? != 0 ]]; &lt;span class=&quot;keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;constant function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-ruvL&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--delete&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--rsh=&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;ssh -p &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;RPORT&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt; -i &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RUSER&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;EHOST&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RPATH&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;LPATH&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/update-music-rsync-so.log &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&amp;gt;&amp;amp;1
&lt;span class=&quot;keyword&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save this script to &lt;code&gt;/data/updateMusic&lt;/code&gt;, make it executable with &lt;code&gt;chmod +x /data/updateMusic&lt;/code&gt;, then run the initial import with &lt;code&gt;/data/updateMusic&lt;/code&gt;. After a while, you’ll have all your computer’s music on your phone. Now, we just need to make it update automatically.&lt;/p&gt;&lt;p&gt;Note: I set up a couple of logs for you. &lt;code&gt;/var/log/update-music.log&lt;/code&gt; has the timestamp of every time it did an update. Also, &lt;code&gt;/var/log/update-music-rsync-so.log&lt;/code&gt; has the output of rsync from each run.&lt;/p&gt;&lt;h2&gt;Cron&lt;/h2&gt;&lt;p&gt;Finally, we need to set up a cronjob. If you followed the instructions so far (and if you’re lucky), you should have everything ready for cron. The biggest pain in my ass was getting cron to coorperate, but the init script earlier should take care of that. Run &lt;code&gt;crontab -e&lt;/code&gt; and write your crontab:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;0 * * * * /data/updateMusic
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nice and simple. Your phone will now sync up your music every hour, on the hour, with your home computer. Here are some possible points for improvement:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Check wlan0 and only sync if it’s up&lt;/li&gt;&lt;li&gt;Log cron somewhere&lt;/li&gt;&lt;li&gt;Alter the update script to do a little bit better about the “fallback”&lt;/li&gt;&lt;li&gt;Sync more than just music&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;After all of this, I now have a nice setup that syncs music to my phone so I can listen to it with Apollo. I might switch away from Apollo, though, it’s pretty buggy. &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Let me know&lt;/a&gt; if you can suggest an alternative music player, or if you get stuck working through this procedure yourself.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Music-syncing-on-Android/</link>
        
        <pubDate>Sat, 24 Aug 2013 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Music-syncing-on-Android/</guid>
      </item>
    
      <item>
        
        
          <title>Custom Music Syncing on Android</title>
          <description>
            &lt;p&gt;I have an HTC One, with CyanogenMod installed. I usually use Spotify, but I’ve been wanting to move away from it for a while. The biggest thing keeping me there was the ease of syncing up with my phone - I added music on my PC and it just showed up on my phone.&lt;/p&gt;&lt;p&gt;So, I finally decided to make it work on my phone without Spotify. You might have success if you aren’t using CyanogenMod, but you definitely need to be rooted and you need to access a root shell on your phone. I was using &lt;code&gt;adb shell&lt;/code&gt; to start with, but it has poor terminal emulation. Instead, I ended up installing an SSH daemon on the phone and just using that. Easier to use vim in such an enviornment.&lt;/p&gt;&lt;p&gt;The end result is that a cronjob kicks off each hour on my phone and runs a script that uses rsync to sync up my phone’s music with my desktop’s music. That’s another thing - a prerequisite of this working is that you have to expose your music to the outside world on an SSH server somewhere.&lt;/p&gt;&lt;p&gt;I’ll tell you how I got it working, then you can see if it works for you. It might take some effort on your part to tweak these instructions to fit your requirements.&lt;/p&gt;&lt;h2&gt;Sanity checks&lt;/h2&gt;&lt;p&gt;Get into your phone’s shell and make sure you have basic things installed. You’ll need to make sure you have:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;bash&lt;/li&gt;&lt;li&gt;cron&lt;/li&gt;&lt;li&gt;ssh&lt;/li&gt;&lt;li&gt;rsync&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you don’t have them, you can probably get them by installing busybox.&lt;/p&gt;&lt;h2&gt;Setting up SSH&lt;/h2&gt;&lt;p&gt;We need to generate a key. I tried using ssh-keygen before, but it had problems with rsync on Android. Instead, we use dropbearkey. Generate your key with &lt;code&gt;dropbearkey -t rsa -f /data/.ssh/id_rsa&lt;/code&gt;. You’ll see the public key echoed to stdout. It’s not saved anywhere for you, so grab it out of your shell and put it somewhere - namely, in the authorized_keys file on the SSH server you plan to pull music from.&lt;/p&gt;&lt;p&gt;At this point, you can probably SSH into the server you want to pull from. Run &lt;code&gt;ssh -i /data/.ssh/id_rsa &amp;lt;your server here&amp;gt;&lt;/code&gt; to double check. Note that this isn’t just for fun - you need to do this to get your server into known_hosts, so we can non-interactively access it.&lt;/p&gt;&lt;h2&gt;Making Android more sane&lt;/h2&gt;&lt;p&gt;Now that this is working, we need to clean up a little before cron will run right. Android is only a “Linux” system in the sense that &lt;code&gt;uname&lt;/code&gt; outputs “Linux”. It grossly ignores the FHS and you need to fix it a little. Figure out how to do a nice init.d script on your phone. For my CyanogenMod install, I can add scripts to &lt;code&gt;/data/local/userlocal.d/&lt;/code&gt; and they’ll be run at boot. Here’s my little script for making Android a little more sane:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/system/bin/sh&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# Making /system rw isn&amp;apos;t strictly needed&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;mount&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;remount,rw&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/system&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;mount&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;remount,rw&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/data/var&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/var&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/system/bin&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/bin&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/data/.ssh&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;/.ssh&lt;/span&gt;
&lt;span class=&quot;constant function&quot;&gt;crond&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update script and initial import&lt;/h2&gt;&lt;p&gt;The following is the script we’ll use to update your phone’s music library.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;comment&quot;&gt;#!/system/xbin/bash&lt;/span&gt;
&lt;span class=&quot;comment&quot;&gt;# Syncs music between a remote computer and this phone&lt;/span&gt;
&lt;span class=&quot;constant property&quot;&gt;RHOST&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;=&amp;lt;remote hostname&amp;gt;
EHOST=&amp;lt;fallback, I use this for connecting from outside my LAN&amp;gt;
RPORT=22
RUSER=&amp;lt;username&amp;gt;
ID=/data/.ssh/id_rsa
RPATH=/path/to/your/remote/music
# Omit the final directory. On my setup, this goes to /sdcard/Music, and my remote is /home/sircmpwn/Music
LPATH=/sdcard

echo &lt;/span&gt;&lt;span class=&quot;embedded constant&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;embedded constant function&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;embedded&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt; /var/log/update-music.log&lt;/span&gt;

&lt;span class=&quot;constant function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-ruvL&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--delete&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--rsh=&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;ssh -p &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;RPORT&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt; -i &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RUSER&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RHOST&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RPATH&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;LPATH&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/update-music-rsync-so.log &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&amp;gt;&amp;amp;1
&lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; [[ &lt;span class=&quot;operator&quot;&gt;$&lt;/span&gt;? != 0 ]]; &lt;span class=&quot;keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;constant function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;-ruvL&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--delete&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;--rsh=&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;ssh -p &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;RPORT&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt; -i &lt;/span&gt;&lt;span class=&quot;string constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;string constant property&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;string constant&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RUSER&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;EHOST&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;RPATH&lt;/span&gt; &lt;span class=&quot;constant operator&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;constant property&quot;&gt;LPATH&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/update-music-rsync-so.log &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&amp;gt;&amp;amp;1
&lt;span class=&quot;keyword&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save this script to &lt;code&gt;/data/updateMusic&lt;/code&gt;, make it executable with &lt;code&gt;chmod +x /data/updateMusic&lt;/code&gt;, then run the initial import with &lt;code&gt;/data/updateMusic&lt;/code&gt;. After a while, you’ll have all your computer’s music on your phone. Now, we just need to make it update automatically.&lt;/p&gt;&lt;p&gt;Note: I set up a couple of logs for you. &lt;code&gt;/var/log/update-music.log&lt;/code&gt; has the timestamp of every time it did an update. Also, &lt;code&gt;/var/log/update-music-rsync-so.log&lt;/code&gt; has the output of rsync from each run.&lt;/p&gt;&lt;h2&gt;Cron&lt;/h2&gt;&lt;p&gt;Finally, we need to set up a cronjob. If you followed the instructions so far (and if you’re lucky), you should have everything ready for cron. The biggest pain in my ass was getting cron to coorperate, but the init script earlier should take care of that. Run &lt;code&gt;crontab -e&lt;/code&gt; and write your crontab:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;0 * * * * /data/updateMusic
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nice and simple. Your phone will now sync up your music every hour, on the hour, with your home computer. Here are some possible points for improvement:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Check wlan0 and only sync if it’s up&lt;/li&gt;&lt;li&gt;Log cron somewhere&lt;/li&gt;&lt;li&gt;Alter the update script to do a little bit better about the “fallback”&lt;/li&gt;&lt;li&gt;Sync more than just music&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;After all of this, I now have a nice setup that syncs music to my phone so I can listen to it with Apollo. I might switch away from Apollo, though, it’s pretty buggy. &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;Let me know&lt;/a&gt; if you can suggest an alternative music player, or if you get stuck working through this procedure yourself.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Music-syncing-on-Androi/</link>
        
        <pubDate>Sat, 24 Aug 2013 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Music-syncing-on-Androi/</guid>
      </item>
    
      <item>
        
        
          <title>You don&apos;t need jQuery</title>
          <description>
            &lt;p&gt;It’s true. You really don’t need jQuery. Modern web browsers can do most of what you want from jQuery, without jQuery.&lt;/p&gt;&lt;p&gt;For example, take &lt;a href=&quot;https://mediacru.sh&quot; target=&quot;_blank&quot;&gt;MediaCrush&lt;/a&gt;. It’s a website I spent some time working on with a friend. It’s actually quite sophisticated - drag-and-drop uploading, uploading via a hidden form, events wired up to links and dynamically generated content, and ajax requests/file uploads, the whole she-bang. It does all of that without jQuery. It’s &lt;a href=&quot;https://github.com/MediaCrush/MediaCrush&quot; target=&quot;_blank&quot;&gt;open source&lt;/a&gt;, if you’re looking for a good example of how all of this can be used in the wild.&lt;/p&gt;&lt;p&gt;Let’s walk through some of the things you like jQuery for, and I’ll show you how to do it without.&lt;/p&gt;&lt;h2&gt;Document Querying with CSS Selectors&lt;/h2&gt;&lt;p&gt;You like jQuery for selecting content. I don’t blame you - it’s really cool. Here’s some code using jQuery:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;div.article p&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;addClass&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;test&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, here’s how you can do it on vanilla JS:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;div.article p&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;test&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Documentation: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document.querySelectorAll&quot; target=&quot;_blank&quot;&gt;querySelectorAll&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/element.classList&quot; target=&quot;_blank&quot;&gt;classList&lt;/a&gt;&lt;/p&gt;&lt;p&gt;This is, of course, a little more verbose. However, it’s probably a lot simpler than you expected. Works in IE 8 and newer - except for classList, which works in IE 10 and newer. You can instead use className, which is a little less flexible, but still pretty easy to work with.&lt;/p&gt;&lt;h2&gt;Ajax&lt;/h2&gt;&lt;p&gt;You want to make requests in JavaScript. This is how you POST with jQuery:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;/path/to/endpoint&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;property&quot;&gt;parameter&lt;/span&gt;: &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;value&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;otherParameter&lt;/span&gt;: &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;otherValue&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;success&lt;/span&gt;: &lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s the same code, without jQuery:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;xhr&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;XMLHttpRequest&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;// A little deceptively named&lt;/span&gt;
&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;xhr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;POST&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;apos;/path/to/endpoint&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;xhr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;responseText&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;formData&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;FormData&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;parameter&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;otherParameter&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;xhr&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Documentation: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest&quot; target=&quot;_blank&quot;&gt;XMLHttpRequest&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Also a bit more verbose than jQuery, but much simpler than you might’ve expected. Now here’s the real kicker: It works in IE 7, and IE 5 with a little effort. IE actually pioneered XHR.&lt;/p&gt;&lt;h2&gt;Animations&lt;/h2&gt;&lt;p&gt;This is where it starts to get more subjective and breaks backwards compatability. Here’s my opinion on the matter of transitions: dropping legacy browser support for fancy animations is acceptable. I don’t think it’s a problem if your website isn’t pretty and animated on older browsers. Keep that in mind as we move on.&lt;/p&gt;&lt;p&gt;I want to animate the opacity of a &lt;code&gt;.foobar&lt;/code&gt; when you hover over it. With jQuery:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;.foobar&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;mouseenter&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;animate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;property&quot;&gt;opacity&lt;/span&gt;: &lt;span class=&quot;number&quot;&gt;0.5&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;mouseleave&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;animate&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;property&quot;&gt;opacity&lt;/span&gt;: &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without jQuery, I wouldn’t do this in Javascript. I’d use the magic of CSS animations:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;css&quot;&gt;.&lt;span class=&quot;property&quot;&gt;foobar&lt;/span&gt; {
    &lt;span class=&quot;property variable&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;opacity&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;type number&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;variable&quot;&gt;linear&lt;/span&gt;;
}

.&lt;span class=&quot;property&quot;&gt;foobar&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;property attribute&quot;&gt;hover&lt;/span&gt; {
    &lt;span class=&quot;property variable&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;foobar&quot;&gt;Hover over this text&lt;/p&gt;
&lt;style&gt;.foobar{transition:opacity 2s linear;font-weight:bold;}.foobar:hover{opacity:0.5;}&lt;/style&gt;
&lt;p&gt;Documentation: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_animations&quot; target=&quot;_blank&quot;&gt;CSS animations&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Much better, eh? Works in IE 10+. You can do much more complicated animations with CSS, but I can’t think of a good demo, so that’s an exercise left to the reader.&lt;/p&gt;&lt;h2&gt;Tree traversal&lt;/h2&gt;&lt;p&gt;jQuery lets you navigate a tree pretty easily. Let’s say you want to find the container of a button and remove all .foobar elements underneath it, upon clicking the button.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;#mybutton&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;constructor constant variable_builtin function_builtin variable function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;.foobar&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice and succinct. I’m sure you can tell the theme so far - the main advantage of jQuery is a less verbose syntax. Here’s how it’s done without jQuery:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;mybutton&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;click&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;foobars&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;parentElement&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&amp;apos;.foobar&amp;apos;&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;foobars&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;variable_builtin&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property&quot;&gt;parentElement&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;property function_method&quot;&gt;removeChild&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;foobars&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;constructor constant variable_builtin function_builtin variable&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A little wordier, but not so bad. Works in IE 9+ (8+ if you don’t use addEventListener).&lt;/p&gt;&lt;h2&gt;In conclusion&lt;/h2&gt;&lt;p&gt;jQuery is, of course, based on JavaScript, and as a result, anything jQuery can do can be done without jQuery. Feel free to &lt;a href=&quot;mailto:drew@ddevault.org&quot; target=&quot;_blank&quot;&gt;ask me&lt;/a&gt; if you’re curious about how I’d do something else without jQuery.&lt;/p&gt;&lt;p&gt;I feel like adding jQuery is one of the first things a web developer does to their shiny new website. It just isn’t really necessary in this day and age. That extra request, 91kb, and load time are probably negligible, but it’s still a little less clean than it could be. There’s no need to go back and rid all of your projects of jQuery, but I’d suggest that for your next one, you try to do without. Keep MDN open in the next tab over and I’m sure you’ll get through it fine.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/You-dont-need-jQuery/</link>
        
        <pubDate>Mon, 19 Aug 2013 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/You-dont-need-jQuery/</guid>
      </item>
    
      <item>
        
        
          <title>Installing Windows 8 - Super-User Guide</title>
          <description>
            &lt;p&gt;&lt;em&gt;This post was originally hosted on my old Blogspot blog. The approximate publication date and title are known but its contents are lost to time. This stub remains for posterity.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Installing-Windows-8-super-user-guide/</link>
        
        <pubDate>Thu, 01 Mar 2012 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Installing-Windows-8-super-user-guide/</guid>
      </item>
    
      <item>
        
        
          <title>LibMinecraft and Partycraft</title>
          <description>
            &lt;p&gt;&lt;em&gt;This post was originally hosted on my old Blogspot blog. The approximate publication date and title are known but its contents are lost to time. This stub remains for posterity.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;LibMinecraft was a .NET library for writing Minecraft servers and clients. You can see what remains of it &lt;a href=&quot;https://web.archive.org/web/20120223233615/https://libminecraft.codeplex.com/&quot; target=&quot;_blank&quot;&gt;archived here&lt;/a&gt;. PartyCraft was a Minecraft server based on LibMinecraft. It’s archived &lt;a href=&quot;https://github.com/ddevault/partycraft&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/LibMinecraft-and-PartyCraft/</link>
        
        <pubDate>Tue, 10 Jan 2012 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/LibMinecraft-and-PartyCraft/</guid>
      </item>
    
      <item>
        
        
          <title>Minecraft on Mango</title>
          <description>
            &lt;p&gt;Yes, you see that right.  My new pet project is making a Minecraft client for Windows Phone 7 that can connect to official Minecraft 1.0 (or 1.0.1) servers. So far, I have connecting done, and I’m working on the immense code required to parse all server messages.  This is only possible thanks to the new &lt;a href=&quot;https://web.archive.org/web/20120305125235/http://bit.ly/wL2FfT&quot; target=&quot;_blank&quot;&gt;Socket&lt;/a&gt; support added to Mango.  I’ve already written an (incomplete) library for connecting to Minecraft servers in C#, but the crucial flaw with porting it to WP7 is that the existing library is syncronous.  Mango unfortunately supports only asyncronous socket communication, so I almost have to start from scratch.&lt;/p&gt;&lt;p&gt;As for now, I’ll keep you guys posted on the progress here on my blog.  Making a full-blown Minecraft client like I intend to is shaky legal ground.  Once I’ve progressed further, I plan on contacting Mojang for approval, then open source it for the remaining development.  One benifit that this has is that it will not (eventually) let you play Minecraft without actually purchasing it from Mojang.&lt;/p&gt;&lt;p&gt;Oh, and if I ever manage to figure out native stuff on WP7, I’ll reverse it and make your phone the server.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Minecraft-on-Mango/</link>
        
        <pubDate>Tue, 03 Jan 2012 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Minecraft-on-Mango/</guid>
      </item>
    
      <item>
        
        
          <title>Miners&apos; Guild Site</title>
          <description>
            &lt;p&gt;As some of you may know, I’m a huge believer in OSS and doing awesome stuff for free.  Of course, I have to make a living, but outside of 9-5, I do a bunch of free stuff.  And when I started playing Minecraft, a friend of mine invited me to his server.  He had a pretty bland website with no domain (you’d get to it by IP).  To express my gratitude at his hosting of a great &lt;a href=&quot;https://web.archive.org/web/20120305114555/http://minecraft.net/&quot; target=&quot;_blank&quot;&gt;Minecraft&lt;/a&gt; server, I offered to clean it up.  He gave me root access to his machines as I bought a domain, and I hopped on the web server (a linux machine) and set up LAMP (Linux/Apache2/MySQL/PHP).  I threw &lt;a href=&quot;https://web.archive.org/web/20120305114555/http://wordpress.com/&quot; target=&quot;_blank&quot;&gt;Wordpress&lt;/a&gt; on it and got down to business customizing styles and writing custom PHP to drive some of the extra functionality.  We wanted a little more, so I also threw &lt;a href=&quot;https://web.archive.org/web/20120305114555/http://www.simplemachines.org/&quot; target=&quot;_blank&quot;&gt;SMF&lt;/a&gt; on there.  The end result is a pretty nice website: &lt;a href=&quot;https://web.archive.org/web/20120305114555/http://minersguild.net/&quot; target=&quot;_blank&quot;&gt;Miners’ Guild&lt;/a&gt;.  I’m definitely satisfied with the end result.&lt;/p&gt;&lt;p&gt;In addition to this, I wrote a bunch of custom server mods.  We didn’t really want to use &lt;a href=&quot;https://web.archive.org/web/20120305114555/http://bukkit.org/&quot; target=&quot;_blank&quot;&gt;Bukkit&lt;/a&gt; (though it is great), so I instead wrote several custom mods myself that add some of the things we needed, such as better banning, item restrictions, chest protection, and - of course - exploding arrows (this can be toggled).  It was great contributing to help make my friend’s server as awesome as possible.  Come visit sometime if you play Minecraft!&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Miners-guild-site/</link>
        
        <pubDate>Thu, 01 Dec 2011 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Miners-guild-site/</guid>
      </item>
    
      <item>
        
        
          <title>Umbraco Certification</title>
          <description>
            &lt;p&gt;I’m officially an Umbraco Certified Developer, with level 2 certification (the highest).  Hooray!  This means I’m now officially an expert on using the great open-source ASP.NET Content Management System &lt;a href=&quot;https://web.archive.org/web/20120305102609/http://umbraco.com/&quot; target=&quot;_blank&quot;&gt;Umbraco&lt;/a&gt;.  Thanks to my employer, &lt;a href=&quot;https://web.archive.org/web/20120305102609/http://www.dsoft-tech.com/&quot; target=&quot;_blank&quot;&gt;DSoft Technology&lt;/a&gt;, for paying for the certification.  This explains the fancy new badge on the right side of my blog.&lt;/p&gt;&lt;p&gt;Short story short: if you are working with Umbraco and need help, I’m your guy.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Umbraco-certification/</link>
        
        <pubDate>Tue, 29 Nov 2011 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Umbraco-certification/</guid>
      </item>
    
      <item>
        
        
          <title>Free Software Abounds</title>
          <description>
            &lt;p&gt;One thousand thanks to all the people that have given me free software!  It’s always nice to be able to develop on the cheap.  Let’s see who I can thank - first of all, &lt;a href=&quot;https://web.archive.org/web/20120305122548/http://blogs.msdn.com/b/techstudent/default.aspx?PageIndex=1&quot; target=&quot;_blank&quot;&gt;Microsoft Student Insiders&lt;/a&gt; hooked me up with a free MSDN account, which is no small feat.  &lt;a href=&quot;https://web.archive.org/web/20120305122548/http://msdn.microsoft.com/en-us/subscriptions/buy.aspx&quot; target=&quot;_blank&quot;&gt;MSDN&lt;/a&gt; gives me virtually all Microsoft software ever, for the lovely price of $11,899.  Thanks to the Student Insiders, I got it for $0.  I highly recommend getting it - perhaps a cheaper package, though - you get support from Microsoft, a ton of software, and more stuff that I could never list in full.  Things that I’ve used MSDN for: Visual Studio 2010 Ultimate, Windows 7 Ultimate, Office 2010.&lt;/p&gt;&lt;p&gt;In addition to free MSDN, &lt;a href=&quot;https://web.archive.org/web/20120305122548/http://www.jetbrains.com/&quot; target=&quot;_blank&quot;&gt;JetBrains&lt;/a&gt; has given me a free license for &lt;a href=&quot;https://web.archive.org/web/20120305122548/http://www.jetbrains.com/resharper/&quot; target=&quot;_blank&quot;&gt;ReSharper&lt;/a&gt;, which is easily the best add-in for Visual Studio 2010 you could ever get.  It does everything you ever wanted Visual Studio to do, and again I highly recommend it.  JetBrains gives out free licenses for open source projects and was impressed by &lt;a href=&quot;https://web.archive.org/web/20120305122548/http://tidenv.codeplex.com/&quot; target=&quot;_blank&quot;&gt;tiDE&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Finally, I’d like to do a call out to &lt;a href=&quot;https://web.archive.org/web/20120305122548/http://codeplex.com/&quot; target=&quot;_blank&quot;&gt;Codeplex&lt;/a&gt;.  All though they haven’t done anything special for me as an individual, they provide a great service to the .NET community.  Codeplex provides hosting for many of my open source projects, including all sorts of source control - SVN, TFS, Mercurial, etc.  You can keep tabs on my &lt;a href=&quot;https://web.archive.org/web/20120305122548/http://www.codeplex.com/site/users/view/sircmpwn&quot; target=&quot;_blank&quot;&gt;Codeplex account&lt;/a&gt; to see all sorts of new and exciting open source projects.  You can only see a couple of projects there now, but I promise that there are a few more projects that are hidden from view at the moment.&lt;/p&gt;&lt;p&gt;In other news, I’ll be picking Windows Phone 7 development back up again, and you can expect lots of information about Mango and all the cool, open-source apps you could ever want.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Free-software-abounds/</link>
        
        <pubDate>Wed, 31 Aug 2011 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Free-software-abounds/</guid>
      </item>
    
      <item>
        
        
          <title>The Cheap Way To Fix a GameBoy Game</title>
          <description>
            &lt;p&gt;So I recently picked up Pokemon Gold on eBay for super cheap because the saving feature was broken.  For those of you who don’t know, Pokemon Gold and games like it store their saved data in a special kind of RAM called SRAM.  Since RAM requires a battery to keep the data intact, these cartridges have a small battery inside.  Usually, when your game stops being able to save, it’s because this battery died.  Unfortunately, the kind of battery they use can be difficult to find.  I decided to use an alternate battery in its place - a AA.&lt;/p&gt;&lt;p&gt;Obviously a AA battery can’t fit inside of a GameBoy cartridge, so I had to do a lot of hacky stuff to make this work.  The first thing I did was scrounge up some equipment.  I don’t have any solder, or a soldering iron, or even any proper wires.  The materials for this build are as follows:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Cheap plastic pen&lt;/li&gt;&lt;li&gt;Lighter&lt;/li&gt;&lt;li&gt;Old noise-cancelling headphones&lt;/li&gt;&lt;li&gt;Speaker wires&lt;/li&gt;&lt;li&gt;Tape&lt;/li&gt;&lt;li&gt;Scissors&lt;/li&gt;&lt;li&gt;AA Battery&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These are all the materials I used for this.  The first thing I did was get the cartridge open.  I took the ink and metal tip out of my pen, and melted down the tip.  While it was still hot, I pressed the tip of the pen onto the screw that held the casing on (it requires a special screwdriver that is difficult to find).  After the melted plastic pen dried, I had a DIY GameBoy cartridge screwdriver.  I removed the screw and opened up the case to reveal the game inside.&lt;/p&gt;&lt;p&gt;Attached to the game was a battery powering the SRAM.  I pried it out of its casing, leaving the contacts in place.  Then I looked for some wiring and found speaker wire.  This I stripped.  I wound some of it around each contact and left it aside.  Then, using the lighter to soften the plastic, and scissors to manipulate it, I put some holes in the side of the casing so the wires could get out.  I put the game back in the case and led the wires out.&lt;/p&gt;&lt;p&gt;I went to look for something to hold the battery with.  After a lot of searching, I stumbled upon some old noise cancelling headphones that had a mount for a single AA battery.  I abused the headphones until just the mount remained, and put a fresh battery inside.  I trapped the leads coming out of the cartridge between the battery’s contacts and the walls of the mount.  I then stuck the cartridge back into my GameBoy Advance and turned it on.&lt;/p&gt;&lt;p&gt;I went through the prompts and chatted up Professor Oak a bit, then finally was dropped into the overworld and saved the game.  I turned off the GameBoy, crossed my fingers, and started it up again.  Hooray!  The game offered me the option to continue, and I was able to save again.&lt;/p&gt;&lt;p&gt;Pics of the build below:&lt;/p&gt;&lt;p&gt;The GameBoy showing the successfully saved game.  The front of the case is to the left, only half of the case is actually inserted.  This allowed me to make sure that the cartridge meets the contacts in the GameBoy itself, but still let me meddle around inside without taking it out.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/gameboy-1.jpg&quot;&gt;&lt;/p&gt;&lt;p&gt;The game, before replacing the front of the cartridge.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/gameboy-2.jpg&quot;&gt;&lt;/p&gt;&lt;p&gt;The cartridge and the battery leading up to it.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/gameboy-3.jpg&quot;&gt;&lt;/p&gt;&lt;p&gt;The final game, completed and ready to play!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/gameboy-4.jpg&quot;&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://drewdevault.com/gameboy-5.jpg&quot;&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Cheap-gameboy-fix/</link>
        
        <pubDate>Sun, 07 Aug 2011 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Cheap-gameboy-fix/</guid>
      </item>
    
      <item>
        
        
          <title>Creating an Integrated Development Environment</title>
          <description>
            &lt;p&gt;The Texas Instruments community does not have a modern, well-working IDE for development.  A while ago, I thought I’d take up the responsibility and create tiDE - Texas Instruments Development Environment.  It’s an open source IDE which, although geared heavily towards TI development, should be easy to adapt to many more languages if you so choose.  I just want to highlight some interesting things about it.&lt;/p&gt;&lt;p&gt;First of all, it has support for a plethora of languages (or at least it will - languages already supported are highlighted): &lt;strong&gt;z80 Assembly&lt;/strong&gt;, &lt;strong&gt;z80 TI-Basic&lt;/strong&gt;, &lt;strong&gt;z80 Axe Parser&lt;/strong&gt;, 68k Assembly, 68k TI-Basic, 68k C, ARM Basic, ARM Assembly, ARM C, and TI-Lua.&lt;/p&gt;&lt;p&gt;tiDE also comes with it’s own z80 TI-83+ emulator, and will eventually have built-in emulators for all the platforms it supports.&lt;/p&gt;&lt;p&gt;Interesting features that the IDE has, that may be extensible to your own programs, that are completed and ready for use as of now:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Syntax Highlighting, Code Completion, and Code Folding provided by &lt;a href=&quot;https://web.archive.org/web/20120305101527/http://www.icsharpcode.net/&quot; target=&quot;_blank&quot;&gt;ICSharpCode&lt;/a&gt;’s open-source Text Editor&lt;/li&gt;&lt;li&gt;Window Docking provided by the open-source &lt;a href=&quot;https://web.archive.org/web/20120305101527/http://sourceforge.net/projects/dockpanelsuite/&quot; target=&quot;_blank&quot;&gt;Dock Panel Suite&lt;/a&gt;&lt;/li&gt;&lt;li&gt;XML-Based Project and File Templates&lt;/li&gt;&lt;li&gt;Code Insight - Provides the ability to learn different things about code, including generating a list of “important places” that the solution explorer uses to navigate straight to different areas of code&lt;/li&gt;&lt;li&gt;Extensible, easy to implement Settings Manager&lt;/li&gt;&lt;li&gt;z80 Core Emulation provided by Benjamin Ryves in a project called “Brazil”&lt;/li&gt;&lt;li&gt;Fast mnomic-based assembly using caching to improve speed&lt;/li&gt;&lt;li&gt;Automatic Substitution - one of our supported languages, TI-Basic, has untypable characters.  When you type “-&gt;”, it automatically becomes “→”&lt;/li&gt;&lt;li&gt;(De)tokenization: taking a series of text and tokenizing it into smaller, byte based commands&lt;/li&gt;&lt;li&gt;Grayscale sprite editing with any number of colors&lt;/li&gt;&lt;li&gt;Automatic generation and presentation of commands provided through code completion&lt;/li&gt;&lt;li&gt;Customizable keyboard shortcuts that map to any clickable object in the IDE&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;All of these features are ready to use right now, and free for anyone to implement in their own projects.  Download the source code at &lt;a href=&quot;http://tidenv.codeplex.com&quot; target=&quot;_blank&quot;&gt;http://tidenv.codeplex.com&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Creating-an-IDE/</link>
        
        <pubDate>Sat, 23 Jul 2011 13:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Creating-an-IDE/</guid>
      </item>
    
      <item>
        
        
          <title>On the Use of Internet Filtering</title>
          <description>
            &lt;p&gt;Wow, it’s been a long time since I’ve made a post.  Sorry about that.&lt;/p&gt;&lt;p&gt;I recently had a conversation with some friends about internet filtering.  It’s a terrible system that tempts parents, bosses, and more to restrict their underlings access to the internet - and it barely works.  There are numerous reasons why I don’t like this system, and think that it poses a severe threat to the internet.&lt;/p&gt;&lt;h2&gt;Filters are the Wrong Solution&lt;/h2&gt;&lt;p&gt;There may be legitimate reasons for a parent to want their children to avoid certain websites on the internet.  However, filters are the wrong way to do it. When a child leaves a filtered home as they come of age, they do not have any knowledge of the internet’s more dangerous places, and certain websites can be very tempting for a young man to visit - the same websites that can be more dangerous.  They also don’t learn about some very important things they should know about the filtered content.  A far better idea is to not block the content, and when the eventually find it, talk to them about it, explain the dangers, and use different means to keep them away.  That way, when they inevitably come out from behind the filter, they are prepared.&lt;/p&gt;&lt;h2&gt;Filters Are Ineffective&lt;/h2&gt;&lt;p&gt;All filtering systems I’ve ever seen are not good at what they do.  They take an impossible task of filtering different parts of a system larger than any person can possibly imagine.  In my previous job, I was working under an internet filter, and every day I would be inhibited by it in my work.  At my current job, I do not have a filter, and I work more efficiently and still get things done.  My high school would filter all student internet, and there were constant complaints about not being able to get school work done around them.&lt;/p&gt;&lt;p&gt;Not only do filters filter out the wrong things, but they also don’t stop all of the right things.  It is easy to go behind a filter and find plenty of websites that are worthy of being blocked.  In addition to that, there are dozens of ways to get around filters.  I can think of 8 ways, off the top of my head, to get around every filter I’ve ever been behind.&lt;/p&gt;&lt;h2&gt;Filters Slow Down Computers&lt;/h2&gt;&lt;p&gt;Upon starting to use a filter, you will notice an immediate increase in your computer’s local resource usage, or the network usage (depending on the type of filter).  There is a lot more effort involved in filtering the internet than there is in accessing it.  A simple explanation of how they work:&lt;/p&gt;&lt;p&gt;Without a filter, you can directly request information from a server.&lt;/p&gt;&lt;p&gt;With a filter (depending on the filter): You try to request information from the server.  The filter interrupts you, and instead directs the request to it’s own private server.  That server downloads the information you requested, and looks up information about what kind of filtering your software has enabled. Then it compares the information you’ve requested with an enormous blacklist of websites that fall into the kind of filters you have enabled.  Then, it looks at the actual information, and attempts to get an idea of what kind of content it has.  If it passes all of these tests, it’s finally sent back to you.&lt;/p&gt;&lt;h2&gt;Filtering is Just Plain Wrong&lt;/h2&gt;&lt;p&gt;Think about all the countries that filter their internet.  Ever hear of the Great Firewall of China?  Nearly all of the countries that filter their user’s internet are dictatorships, or have some other hostile form of government.  If you’ve ever imposed a filter on someone, you haven’t really thought twice about it.  But when you talk politics with your friends, you’ve probably expressed pity for the people who are restricted by their government.  If you haven’t installed a filter - good for you, and you can see what my point is here.&lt;/p&gt;&lt;p&gt;Filtering internet is just a bad idea - it is morally wrong and it doesn’t work properly.  It stops the people under the filter from effectively using the internet, and it can be circumvented with moderate effort (ask my parents - they’ve used about 6 different filters after I got around each one, and I can still get around #6 if I tried).  When schools filter content, a lot of what gets filtered is games - but the kids who are off task should feel the repercussions when they see their report cards.  There are many perfectly legitimate times when a student would want to play games after finishing their assigned work.&lt;/p&gt;&lt;p&gt;Don’t use them.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/On-the-use-of-internet-filtering/</link>
        
        <pubDate>Sat, 23 Jul 2011 12:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/On-the-use-of-internet-filtering/</guid>
      </item>
    
      <item>
        
        
          <title>Redmond with the Microsoft Student Insiders</title>
          <description>
            &lt;p&gt;I was recently in Redmond (Microsoft HQ), hanging out with the rest of the Microsoft Student Insiders.  Microsoft was kind enough to pay for my flights, hotel, and other expenses, which was great.  It really allowed me an opportunity to just enjoy what I was there for - meeting new people and checking out the next big things.  I was very happy to see their &lt;a href=&quot;https://web.archive.org/web/20120305103908/http://www.microsoft.com/presspass/presskits/mshome/default.aspx&quot; target=&quot;_blank&quot;&gt;Home of the Future&lt;/a&gt; and their very cool &lt;a href=&quot;https://web.archive.org/web/20120305103908/http://www.officelabs.com/projects/envisioninglab/Pages/default.aspx&quot; target=&quot;_blank&quot;&gt;Envisioning Lab&lt;/a&gt;. They also invited me to shop at the Microsoft Employee Store, where I was happy to make a few purchases at a great price.  I also met quite a few cool people while I was there, and was able to sit down and chat with a developer on Windows Phone 7, and testers for Sharepoint and Windows Server.  I was quite impressed with some of the latest upcoming technology at Microsoft, and looking forward to spending more time with the rest of the Student Insiders.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Redmond-with-the-Insiders/</link>
        
        <pubDate>Mon, 07 Mar 2011 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Redmond-with-the-Insiders/</guid>
      </item>
    
      <item>
        
        
          <title>Post-Session: Windows Phone 7 @ RMTT</title>
          <description>
            &lt;p&gt;Thanks to everyone who dropped by and saw my talk on Getting the Most Out of Silverlight for WP7 at the Rocky Mountain Tech Trifecta.  Just a few things you might like to check out:&lt;/p&gt;&lt;h2&gt;Slide Deck&lt;/h2&gt;&lt;p&gt;You can pick up the slide deck right here: &lt;a href=&quot;http://tinyurl.com/5utyofc&quot; target=&quot;_blank&quot;&gt;http://tinyurl.com/5utyofc&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Source Code&lt;/h2&gt;&lt;p&gt;The source code for out Ultimate Task Manager is right here: &lt;a href=&quot;http://tinyurl.com/6fuphmy&quot; target=&quot;_blank&quot;&gt;http://tinyurl.com/6fuphmy&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Do whatever you want with it, but if you make it better, I’d love to hear about it! It’s not too detailed, but it does a pretty decent job of showing off WP7.&lt;/p&gt;&lt;h2&gt;Other Resources&lt;/h2&gt;&lt;p&gt;Here’s some pretty sweet stuff you can check out:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Accelerometer Shake Detection: I didn’t have time to integrate this into our application, but it’s pretty cool.  Mark Arteaga has a nice post about shake detection.  &lt;a href=&quot;http://tinyurl.com/5wg8tqx&quot; target=&quot;_blank&quot;&gt;http://tinyurl.com/5wg8tqx&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Bing Services: I mentioned that you can access Bing Services from the phone to geocode addresses.  Not only does it do that, but a lot more stuff as well.  I didn’t have the extra time to go over this, but here’s some information if you’re interested: &lt;a href=&quot;http://tinyurl.com/6dbdxjm&quot; target=&quot;_blank&quot;&gt;http://tinyurl.com/6dbdxjm&lt;/a&gt;&lt;/li&gt;&lt;li&gt;WP7 Tasks: A couple people asked about tasks in WP7.  Check out Tim Binkley-Jones’ presentation at 4:15, or get the scoop here: &lt;a href=&quot;http://tinyurl.com/5turcpk&quot; target=&quot;_blank&quot;&gt;http://tinyurl.com/5turcpk&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Come back in a couple days and I’ll have a video of the presentation posted for anyone who couldn’t make it.  Post any questions you may have below, and please rate the session here: &lt;a href=&quot;http://tinyurl.com/5slnaja&quot; target=&quot;_blank&quot;&gt;http://tinyurl.com/5slnaja&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Post-Session-WP7-at-RMTT/</link>
        
        <pubDate>Sat, 05 Mar 2011 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Post-Session-WP7-at-RMTT/</guid>
      </item>
    
      <item>
        
        
          <title>Rocky Mountain Tech Trifecta</title>
          <description>
            &lt;p&gt;Coming soon, I’ll be giving a presentation on Windows Phone 7 with Silverlight at the Rocky Mountain Tech Trifecta.  If you can get to Denver on March 5th, it’s a free event and you’ll learn a ton.  Be there by 1:15PM to check out my talk - here’s the description:&lt;/p&gt;&lt;h2&gt;Get the Most Out of Silverlight for WP7&lt;/h2&gt;&lt;p&gt;The Windows Phone 7 provides a great platform for rich, interactive applications with Silverlight 4.0 and XNA. Developers can create amazing applications in small amounts of time using the same powerful tools they already use. The phone provides hardware such as an accelerometer, GPS, and more, and this presentation will cover how to utilize this hardware.&lt;/p&gt;&lt;p&gt;I’ll see you there!  Come back here after the Trifecta for the slide deck and code downloads.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://rmtechtrifecta.com/Home&quot; target=&quot;_blank&quot;&gt;http://rmtechtrifecta.com/Home&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Rocky-Mountain-Tech-Trifecta/</link>
        
        <pubDate>Fri, 25 Feb 2011 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Rocky-Mountain-Tech-Trifecta/</guid>
      </item>
    
      <item>
        
        
          <title>Colorado Springs Give Camp</title>
          <description>
            &lt;p&gt;I recently participated in the Colorado Springs Give Camp, which is a way for charities and other non-profit organizations to get customized solutions for their needs from professional developers.  A group of volunteer professional developers meet over a weekend, and put in work to meet whatever software-related needs a non-profit organization may want.  This time around, we made a website for Crossfire Ministries, as well as a solution for tracking donations and volunteers.&lt;/p&gt;&lt;p&gt;If you want to check out the work we did, you can see the old version of their website here: &lt;a href=&quot;http://www.crossfireministries.com/cf_old&quot; target=&quot;_blank&quot;&gt;http://www.crossfireministries.com/cf_old&lt;/a&gt;, and the new one here: &lt;a href=&quot;http://www.crossfireministries.com/&quot; target=&quot;_blank&quot;&gt;http://www.crossfireministries.com/&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you like the style of the website, you have me to thank - I was in charge of the styling and templates, as well as designing the fancy banner across the top.  However, most of the work was done by the rest of the volunteers, who moved the old content over and trained the Crossfire Ministries staff with the use of the software.  The solution for managing the volunteers and donations was also done by a separate team, who especially deserve credit when their work is behind the scenes.&lt;/p&gt;&lt;p&gt;The website is powered by a very good open source, cross-platform ASP.NET website solution called &lt;a href=&quot;https://web.archive.org/web/20120305151542/http://www.mojoportal.com/&quot; target=&quot;_blank&quot;&gt;MojoPortal&lt;/a&gt;. I can’t praise MojoPortal enough - it made it outrageously easy to get the basic framework of the website up and running.&lt;/p&gt;&lt;p&gt;If you want to join us at the next Give Camp, you can volunteer at the Colorado Springs Give Camp website: &lt;a href=&quot;http://www.springsgivecamp.org/&quot; target=&quot;_blank&quot;&gt;http://www.springsgivecamp.org/&lt;/a&gt;.  And finally, a big thanks to News First 5, who covered the event: &lt;a href=&quot;http://www.newsfirst5.com/videoplayer/?video_id=5579&amp;categories=&quot; target=&quot;_blank&quot;&gt;http://www.newsfirst5.com/videoplayer/?video_id=5579&amp;categories=&lt;/a&gt;&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Colorado-Springs-Give-Camp/</link>
        
        <pubDate>Thu, 27 Jan 2011 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Colorado-Springs-Give-Camp/</guid>
      </item>
    
      <item>
        
        
          <title>Everything you want out of WP7</title>
          <description>
            &lt;p&gt;The brand new Windows Phone 7 is a great device, and presents a great platform for developers to host applications in Silverlight and XNA, and comes with a lot of great stuff for developers.  But what great stuff does it come with? Well, here’s a list:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Accelerometer&lt;/li&gt;&lt;li&gt;GPS&lt;/li&gt;&lt;li&gt;Microphone&lt;/li&gt;&lt;li&gt;Camera (sort of)&lt;/li&gt;&lt;li&gt;User pictures (sort of)&lt;/li&gt;&lt;li&gt;Limited Internet Access&lt;/li&gt;&lt;li&gt;Modify “Live Tiles” (sort of)&lt;/li&gt;&lt;li&gt;Utilize “toast” notifications (sort of)&lt;/li&gt;&lt;li&gt;Multitouch&lt;/li&gt;&lt;li&gt;Maps (SL Only)&lt;/li&gt;&lt;li&gt;SMS Messaging&lt;/li&gt;&lt;li&gt;Email&lt;/li&gt;&lt;li&gt;Contacts&lt;/li&gt;&lt;li&gt;Silverlight Styles&lt;ul&gt;&lt;li&gt;AppBar&lt;/li&gt;&lt;li&gt;Pivot&lt;/li&gt;&lt;li&gt;Panorama&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;All of this stuff is really cool to play around with in an application.  Let’s see how we can use it.  This is intended to give you a quick and dirty overview of all the features so you can get a general view of how to use it in applications.&lt;/p&gt;&lt;h2&gt;Accelerometer&lt;/h2&gt;&lt;p&gt;An accelerometer gives you information about the physical orientation of the phone. It could be used for a level application, for example.  In order to use it, you have to create a new Microsoft.Devices.Sensors.Accelerometer.  You have to hook up to the ReadingChanged event to get information back.  However, this returns a Vector3, which is an XNA class, so be sure to add XNA references to any Silverlight project you use it in.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/ff431809%28VS.92%29.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;GPS&lt;/h2&gt;&lt;p&gt;The phone’s GPS gives you access to the user’s physical location when you poll it for data.  There are also several other classes associated with it that give you useful methods, such as the ability to calculate distance between coordinates.  To access the GPS, you need to set up a System.Device.Location.GeoCoordinateWatcher, and hook up to the PositionChanged event.  This event returns a Location that includes the user’s spatial coordinates.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/system.device.location.geocoordinatewatcher.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Microphone&lt;/h2&gt;&lt;p&gt;There are a number of interesting applications for the microphone, which can be accessed through Microsoft.Xna.Framework.Audio.Microphone.  Use Microphone.BufferLength to change the length of recording, and you can use Microphone.Start() and Microphone.Stop() to control recording.  The data is 16-bit Mono PCM, which can be fed directly into DynamicSoundEffectInstance.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.audio.microphone.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Camera/Pictures&lt;/h2&gt;&lt;p&gt;Accessing the camera is more complicated.  It is impossible for 3rd party developers to get a direct feed from the camera, but you can use tasks to get the user to take a static image.  Microsoft.Phone.Tasks.CameraCaptureClass provides the functionality to do this.  You can hook up to Completed to get the image back (JPG format), and call Show() to get the photo.&lt;/p&gt;&lt;p&gt;The exact same goes for getting user photos.  The PhotoChooserTask allows the user to pick a picture to give to the application, and it is returned through the Completed event.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks.cameracapturetask%28VS.92%29.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Internet Access&lt;/h2&gt;&lt;p&gt;The Windows Phone 7 is locked down for quite a bit of internet access, but allows you to perform HTTP requests the same way you would on a desktop application.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/system.net.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Live Tiles and Toast Notifications&lt;/h2&gt;&lt;p&gt;Because your application cannot run in the background, you have to use a different method for getting notifications to the user while they are not using your application.  This is done via an external service that pushes data to the phone, in the form of a Live Tile update, a Toast notification, or raw data. This is a complex procedure, and more info can be found here:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ff941124(VS.92).aspx&quot; target=&quot;_blank&quot;&gt;http://msdn.microsoft.com/en-us/library/ff941124(VS.92).aspx&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Multitouch&lt;/h2&gt;&lt;p&gt;The Windows Phone 7 provides a multitouch screen capable of detecting four points on the screen at once.  In Silverlight, multitouch support is built in. In XNA, however, you can get this information in the same way as the Zune HD: via the Microsoft.Xna.Framework.Input.TouchPanel class.  You can use GetState(), which returns a TouchCollection that you can enumerate to get all of the places on-screen that are currently being touched.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.touch.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Maps (Silverlight Only)&lt;/h2&gt;&lt;p&gt;In Silverlight, you can use the Bing Maps control to show the user a map.  You can customize this control with pins and more, and feed it GeoCoordinate data to change the location.  It is quite versatile, and can be used in a number of applications.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/ee681884.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;SMS Messaging (Texting)&lt;/h2&gt;&lt;p&gt;One of the primary uses for a cell phone is texting, and you can access this ability through your application.  Using the SmsComposeTask class in Microsoft.Phone.Tasks, you can send text messages to any phone number.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks.smscomposetask%28v=VS.92%29.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Email&lt;/h2&gt;&lt;p&gt;Along a similar idea is the EmailComposeTask, which can be used in a similar way to construct emails.  You can set the body of the email, the To, CCs, and the Subject, which the user will approve before sending.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20120305160351/http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks.emailcomposetask%28v=VS.92%29.aspx&quot; target=&quot;_blank&quot;&gt;More Info&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Tasks&lt;/h2&gt;&lt;p&gt;These tasks are not the only ones.  You can find information about all of the tasks here, including getting contact info:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks(VS.92).aspx&quot; target=&quot;_blank&quot;&gt;http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks(VS.92).aspx&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Silverlight Styles&lt;/h2&gt;&lt;p&gt;The Windows Phone 7 has some pretty stylin’ themes, and as a Silverlight developer, you can take advantage of that.  App Templates in Visual Studio 2010 come pre-packaged as one of the following page types:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Application Page&lt;ul&gt;&lt;li&gt;This is a simple page with no fancy bits or pieces.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Pivot Page&lt;ul&gt;&lt;li&gt;This page is similar to the settings page in WP7, where you have several headers, each with different content, to scroll through horizontally.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Panorama Page&lt;ul&gt;&lt;li&gt;The panorama pages are like the Pictures app that comes with WP7, they have a large scrolling picture in the background of a pivot page.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;AppBar&lt;/h2&gt;&lt;p&gt;The last big feature of WP7 is the AppBar, which is a small bar at the bottom of the page users can interact with.  It can slide up to a larger menu, or collapse for a simple menu with icons.&lt;/p&gt;&lt;p&gt;You can customize this appbar in your own Silverlight application, as described here:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ff431801(VS.92).aspx&quot; target=&quot;_blank&quot;&gt;http://msdn.microsoft.com/en-us/library/ff431801(VS.92).aspx&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Hopefully this list will provide an easy to use reference to find anything you need to do on Windows Phone 7.  It isn’t intended to tell you everything you need to know, but it should give you a good idea of what WP7 is capable of, and provide easy access to more information.  Come back again later, I’ll be going over some of these items in more detail so that you can have your application on its feet in no time.&lt;/p&gt;&lt;h2&gt;Windows Phone 7 for Students&lt;/h2&gt;&lt;p&gt;And finally, students who are interested in developing applications for Windows Phone 7 can do so - for free.  The Microsoft DreamSpark Program allows students to get free access to professional development tools - including Visual Studio 2010 Professional.  With the release of WP7, Microsoft extended the program to waive the $100 WP7 Marketplace developer account fee for students through DreamSpark.  This means that students can submit 5 applications to the Windows Phone 7 Marketplace for free, and start making money from them without paying a penny.  But beware - you must be at least 18 to qualify for a developer account, so some high school students will be left out for a couple years.&lt;/p&gt;
            <div class="footnotes" role="doc-endnotes"></div>
          </description>
          <link>https://drewdevault.com/blog/Everything-you-want-out-of-WP7/</link>
        
        <pubDate>Sat, 11 Dec 2010 00:00:00 +0000</pubDate>
        <guid>https://drewdevault.com/blog/Everything-you-want-out-of-WP7/</guid>
      </item>
    
  </channel>
</rss>
