<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/assets/site_slatecave/atom_to_html.xslt"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>slatecave.net</title>
    <subtitle>Slatians hideout</subtitle>
    <link rel="self" type="application/atom+xml" href="https://slatecave.net/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://slatecave.net"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2024-01-09T00:00:00+00:00</updated>
    <id>https://slatecave.net/atom.xml</id>
    <entry xml:lang="en">
        <title>Group based Permissions using polkit</title>
        <published>2024-01-09T00:00:00+00:00</published>
        <updated>2024-01-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/networkmanager-group-using-polkit/"/>
        <id>https://slatecave.net/blog/networkmanager-group-using-polkit/</id>
        
        <summary type="text">Access things like NetworkManager based on UNIX groups.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/networkmanager-group-using-polkit/">&lt;h2 id=&quot;what-and-why&quot;&gt;What and why?&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m the kind of creature that really likes NetworkManager and avoids systemd when possible.&lt;&#x2F;p&gt;
&lt;p&gt;The problem arises that, when one isn&#x27;t using (e)logind things like being able to configure networks don&#x27;t work anymore. Reason being that polkit is configured to only allow these actions for logged in sessions, but nothing told it that a session is active.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily those rules are not set in stone and one can reconfigure polkit however they like.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; NetworkManager only supports polkit for configuring who can do what. This is okay because it is supposed to manage networks, not permissions.&lt;&#x2F;p&gt;
&lt;p&gt;For my purposes I want to be able to send basic commands to NetworkManager because I&#x27;m in the &lt;code&gt;wheel&lt;&#x2F;code&gt; (aka. &lt;code&gt;sudo&lt;&#x2F;code&gt;) group, independent of me being logged in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;understanding-polkit&quot;&gt;Understanding polkit&lt;&#x2F;h2&gt;
&lt;p&gt;This was the first time I messed with polkit which is very well documented in the &lt;a href=&quot;https:&#x2F;&#x2F;man.archlinux.org&#x2F;man&#x2F;polkit.8&quot;&gt;polkit(8) manpage&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Polkit is a service with its only task being to answer one question&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is &lt;i&gt;subject&lt;&#x2F;i&gt; allowed to do &lt;i&gt;action&lt;&#x2F;i&gt;?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The &lt;i&gt;subject&lt;&#x2F;i&gt; is roughly equivalent to a user.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;i&gt;action&lt;&#x2F;i&gt; is a more or less rough description of what the subject wants to do identified by an &lt;i&gt;id&lt;&#x2F;i&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Actions are described by XML-Files in &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;polkit-1&#x2F;actions&#x2F;&lt;&#x2F;code&gt;. These contain localised names, icon-names and some sane default permissions for each &lt;i&gt;action&lt;&#x2F;i&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In addition to actions and the default permissions, polkit supports rules written in JavaScript. These live in files in &lt;code&gt;&#x2F;etc&#x2F;polkit-1&#x2F;rules.d&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is also &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;polkit-1&#x2F;rules.d&#x2F;&lt;&#x2F;code&gt; but that one is intended for packages. As an admin &lt;code&gt;&#x2F;etc&lt;&#x2F;code&gt; is the preferred place.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;networkmanager-rules&quot;&gt;NetworkManager rules&lt;&#x2F;h2&gt;
&lt;p&gt;Before writing the rule I need the action ids that are used to configure NetworkManager. These can be found by searching the &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;polkit-1&#x2F;actions&#x2F;org.freedesktop.NetworkManager.policy&lt;&#x2F;code&gt; file for all rules that work without any extra authentication given an active session.&lt;&#x2F;p&gt;
&lt;p&gt;The results are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.enable-disable-network&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.enable-disable-wifi&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.enable-disable-wimax&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.enable-disable-wwan&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.network-control&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.settings.modify.own&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.wifi.scan&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.wifi.share.open&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;org.freedesktop.NetworkManager.wifi.share.protected&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;polkit-debug-mode&quot;&gt;Polkit debug mode&lt;&#x2F;h2&gt;
&lt;p&gt;To save you some frustration with debugging: Polkit has a debug mode which is probably turned off when launched through your service manager of choice using the &lt;code&gt;--no-debug&lt;&#x2F;code&gt; option.&lt;&#x2F;p&gt;
&lt;p&gt;To get the debug output.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Stop the &lt;code&gt;polkit&lt;&#x2F;code&gt; service&lt;&#x2F;li&gt;
&lt;li&gt;Run &lt;code&gt;&#x2F;usr&#x2F;lib&#x2F;polkit-1&#x2F;polkitd&lt;&#x2F;code&gt; as root in a terminal&lt;&#x2F;li&gt;
&lt;li&gt;Start the service again when done with debugging&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Polkit will drop privileges automatically after setting itself up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-a-rule&quot;&gt;Adding a rule&lt;&#x2F;h2&gt;
&lt;p&gt;According to the manual I should be able to just place a &lt;code&gt;.rules&lt;&#x2F;code&gt; file in &lt;code&gt;&#x2F;etc&#x2F;polkit&#x2F;rules.d&#x2F;&lt;&#x2F;code&gt; and polkit will pick it up automatically. If you have polkit in debug mode you&#x27;ll even get feedback whether your rules are valid JavaScript or not.&lt;&#x2F;p&gt;
&lt;p&gt;For the NetworkManager scenario my script adds a rule function that first checks if the action id is in an array of allowed actions, then checks if the &lt;i&gt;subject&lt;&#x2F;i&gt; is in the &lt;code&gt;wheel&lt;&#x2F;code&gt; group and if so returns that the action is allowed.&lt;&#x2F;p&gt;
&lt;p&gt;If the rule doesn&#x27;t return a result polkit will fall back to other rules and eventually the defaults.&lt;&#x2F;p&gt;
&lt;p&gt;Translated to JavaScript the result is the following.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript z-code&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;z-source z-ts&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;polkit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;addRule&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-expression z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-ts&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-parameters z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-begin z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-ts&quot;&gt;action&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-parameter z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-parameter z-ts&quot;&gt;subject&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-end z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt; 
	&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;var&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;allowed_actions&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-array z-literal z-ts&quot;&gt; &lt;span class=&quot;z-meta z-brace z-square z-ts&quot;&gt;[&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.enable-disable-network&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.enable-disable-wifi&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.enable-disable-wimax&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.enable-disable-wwan&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.network-control&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.settings.modify.own&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.wifi.scan&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.wifi.share.open&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;org.freedesktop.NetworkManager.wifi.share.protected&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-brace z-square z-ts&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;allowed_actions&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-ts&quot;&gt;indexOf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;action&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;id&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-relational z-ts&quot;&gt;&amp;gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;subject&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;isInGroup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;wheel&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
			&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;polkit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-property z-ts&quot;&gt;YES&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;other-things-polkit-rules-could-do&quot;&gt;Other Things polkit Rules could do&lt;&#x2F;h2&gt;
&lt;p&gt;By testing for an action id prefix and (non-)membership of a group you could also deny access to a service by returning &lt;code&gt;polkit.Result.NO&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Polkit can also call external commands and make a decision based on success or failure of a command.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;man.archlinux.org&amp;#x2F;man&amp;#x2F;polkit.8#Authorization_Rules_Examples&quot;&gt;
		The manual has very good examples.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bye&quot;&gt;Bye&lt;&#x2F;h2&gt;
&lt;p&gt;I hope that was a useful bit of information about a thing that mostly stays out of the way and comes with an extra bit of suprise when it doesn&#x27;t work.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Pretty Atom-Feed Previews with XSLT</title>
        <published>2023-12-22T00:00:00+00:00</published>
        <updated>2023-12-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/atom-xslt/"/>
        <id>https://slatecave.net/blog/atom-xslt/</id>
        
        <summary type="text">Adding some XSLT to my Atom-Feeds to make them behave like Webpages in Browsers</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/atom-xslt/">&lt;h2 id=&quot;atom-and-xslt-what&quot;&gt;Atom and XSLT what?&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ll assume you already know what &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Atom_(web_standard)&quot;&gt;Atom-Feeds&lt;&#x2F;a&gt; and XML are and that you have some HTML skills.&lt;&#x2F;p&gt;
&lt;p&gt;As an Atom-Feed is simply an XML-File on the web-server it makes sense &lt;a href=&quot;&#x2F;about&#x2F;feeds&quot;&gt;linking to it&lt;&#x2F;a&gt;, unfortunately the Atom-XML in the Browser isn&#x27;t pretty to look at.&lt;&#x2F;p&gt;
&lt;p&gt;This is where &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;XSLT&quot;&gt;XSLT (Extensible Stylesheet Language Transformation)&lt;&#x2F;a&gt; steps in. XSLT is a standardised way to turn an XML document into another and, as it turns out it is &lt;a href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xsl_transformation.asp&quot;&gt;built into every major Web-Browser&lt;&#x2F;a&gt;, meaning it works perfectly well with my static site setup.&lt;&#x2F;p&gt;
&lt;p&gt;This way one can deliver a standards compliant Atom-Feed that gets rendered into a preview for humans when viewed with a Browser.&lt;&#x2F;p&gt;
&lt;p&gt;Awesome, lets do that!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If you&#x27;ve ever seen the message &lt;q&gt;This XML file does not appear to have any style information associated with it. The document tree is shown below.&lt;&#x2F;q&gt; the Browser means XSLT, not CSS.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-an-atom-feed-with-a-stylesheet&quot;&gt;Building an Atom-Feed with a Stylesheet&lt;&#x2F;h2&gt;
&lt;p&gt;To get the Browser to load a stylesheet one has to add tell it where it is by adding a line like following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-comment z-block z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-xml&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt; &amp;lt;?xml … header here &lt;span class=&quot;z-punctuation z-definition z-comment z-end z-xml&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-preprocessor z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;xml-stylesheet&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;text&#x2F;xsl&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;href&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&#x2F;assets&#x2F;site_slatecave&#x2F;atom_to_html.xslt&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-block z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-xml&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt; Rest of feed here &lt;span class=&quot;z-punctuation z-definition z-comment z-end z-xml&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For the slatecave I&#x27;ve added the line to &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;site-source.slatecave-net&#x2F;src&#x2F;commit&#x2F;a7ffd694630a425bec04c35b09adcd7461dc9929&#x2F;templates&#x2F;atom.xml#L2&quot;&gt;my atom feed template&lt;&#x2F;a&gt; and put a very minimalistic stylesheet into the static files.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I didn&#x27;t have this template before this project so I customised &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;blob&#x2F;38199c125501e9ff0e700e96adaca72cc3f25d2b&#x2F;components&#x2F;templates&#x2F;src&#x2F;builtins&#x2F;atom.xml&quot;&gt;Zola&#x27;s builtin template&lt;&#x2F;a&gt; a little.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If you want to use XSLT 2.0 functions (mainly &lt;code&gt;replace()&lt;&#x2F;code&gt; and &lt;code&gt;tokenize()&lt;&#x2F;code&gt;) you have to set &lt;code&gt;version&lt;&#x2F;code&gt; to &lt;code&gt;2.0&lt;&#x2F;code&gt; on the &lt;code&gt;xsl:stylesheet&lt;&#x2F;code&gt; tag.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A very minimal XSLT-Sheet that results in a hello-world message.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-preprocessor z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;xml&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;version&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;1.0&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;encoding&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;UTF-8&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-namespace z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;stylesheet&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;version&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;1.0&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-entity z-other z-attribute-name z-namespace z-xml&quot;&gt;xmlns&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;XSL&#x2F;Transform&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-namespace z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;output&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;method&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;html&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;encoding&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;utf-8&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;indent&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;yes&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-namespace z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;template&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;match&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&#x2F;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;html&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;lang&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;en&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;body&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Hello from the XSLT-Sheet!&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;body&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;html&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-namespace z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;template&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-namespace z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;stylesheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;getting-content-from-the-atom-feed&quot;&gt;Getting Content from the Atom-Feed&lt;&#x2F;h2&gt;
&lt;p&gt;To get content from the original XML one can use an &lt;code&gt;&amp;lt;xsl:value-of select=&amp;quot;&#x2F;xpath&#x2F;goes&#x2F;here&amp;quot; &#x2F;&amp;gt;&lt;&#x2F;code&gt; tag.&lt;&#x2F;p&gt;
&lt;p&gt;This tag replaces itself with the content of the first tag returned by the XPath in the &lt;code&gt;select&lt;&#x2F;code&gt; attribute.&lt;&#x2F;p&gt;
&lt;p&gt;!note: &lt;b&gt;What is XPath?&lt;&#x2F;b&gt; XPath is a way to select tags from an XML Document, it works a bit like a Filepath and a bit like a CSS Selector.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;&amp;#x2F;notebook&amp;#x2F;xpath&quot;&gt;
		See my XPath Cheatsheet.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;To spare you the headache: Don&#x27;t forget to declare and use the &lt;code&gt;atom&lt;&#x2F;code&gt; namespace on the XSLT-Sheet.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The new opening tag of the XSLT-Sheet.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-namespace z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;stylesheet&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;version&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;1.0&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-entity z-other z-attribute-name z-namespace z-xml&quot;&gt;xmlns&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;1999&#x2F;XSL&#x2F;Transform&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-entity z-other z-attribute-name z-namespace z-xml&quot;&gt;xmlns&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;atom&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;2005&#x2F;Atom&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;Nodes in the Atom feed can then be accessed like the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-namespace z-xml&quot;&gt;xsl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;value-of&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;select&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&#x2F;atom:feed&#x2F;atom:title&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that out of the way you follow the the &lt;a href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xsl_intro.asp&quot;&gt;w3schools XSLT Tutorial&lt;&#x2F;a&gt; to create such a sheet yourself.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;codeberg.org&amp;#x2F;slatian&amp;#x2F;site-source.slatecave-net&amp;#x2F;src&amp;#x2F;commit&amp;#x2F;a7ffd694630a425bec04c35b09adcd7461dc9929&amp;#x2F;static&amp;#x2F;assets&amp;#x2F;site_slatecave&amp;#x2F;atom_to_html.xslt&quot;&gt;
		The slatecave.net Atom to HTML XSLT-Sheet
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;content-security-policy&quot;&gt;Content Security Policy&lt;&#x2F;h2&gt;
&lt;p&gt;After Uploading I noticed that it isn&#x27;t working. A quick look at the debugging tools reveals that an XSLT sheet is treated like a script which was disabled, because the slatecave doesn&#x27;t use any JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;Setting &lt;code&gt;script-src &#x27;self&#x27;;&lt;&#x2F;code&gt; allows loading and executing the XSLT-Sheet resulting in some &lt;a href=&quot;&#x2F;atom.xml&quot;&gt;pretty Atom-Feeds&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;that-s-it&quot;&gt;That&#x27;s it!&lt;&#x2F;h2&gt;
&lt;p&gt;I hope that information was useful or at least interesting to you!&lt;&#x2F;p&gt;
&lt;p&gt;This is more a braindump style of post, let me know how that went.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>XML XPath</title>
        <published>2023-12-22T00:00:00+00:00</published>
        <updated>2023-12-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/xpath/"/>
        <id>https://slatecave.net/notebook/xpath/</id>
        
        <summary type="text">Query elements from XML Documents, on the CLI, in the Browser, everywhere else</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/xpath/">&lt;p&gt;XPath is a way to select tags from an XML Document, it works a bit like a File-path and a bit like a CSS-Selector.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-document&quot;&gt;Example Document&lt;&#x2F;h2&gt;
&lt;p&gt;For the Examples on this page the following XML-Document is assumed.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-preprocessor z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;xml&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;version&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;1.0&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;encoding&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;UTF-8&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;catalog&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;An Overview of Dataformats.&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Describes Dataformats&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;item&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;JSON&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Data Serialization&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;link&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;website&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;href&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;https:&#x2F;&#x2F;www.json.org&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;link&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;wikipedia&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;href&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;JSON&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;item&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;item&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;XML&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Documents&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;link&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;website&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;href&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;https:&#x2F;&#x2F;www.xml.com&#x2F;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;link&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;wikipedia&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;href&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;XML&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;item&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;catalog&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;querying-nodes&quot;&gt;Querying Nodes&lt;&#x2F;h2&gt;
&lt;p&gt;XPath works like a file-path.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; To address the &lt;code&gt;title&lt;&#x2F;code&gt; tag (&lt;i&gt;node&lt;&#x2F;i&gt; in XPath terminology).&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;catalog&#x2F;title
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If you are inside a context (i.e. one of the &lt;code&gt;item&lt;&#x2F;code&gt; elements) you can also use it like a relative file-path: &lt;code&gt;use&lt;&#x2F;code&gt;, &lt;code&gt;.&#x2F;name&lt;&#x2F;code&gt;, &lt;code&gt;..&#x2F;title&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For wildcards on a single level one can use a &lt;code&gt;*&lt;&#x2F;code&gt; for the tag name.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Same as previous but with a wildcard.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;*&#x2F;title
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By using &lt;code&gt;&#x2F;&#x2F;&lt;&#x2F;code&gt; one can select all elements matching the following description no matter where in the document they are.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Select &lt;em&gt;all&lt;&#x2F;em&gt; three &lt;code&gt;use&lt;&#x2F;code&gt; tags.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F;use
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Using the &lt;code&gt;&#x2F;&#x2F;&lt;&#x2F;code&gt; like &lt;code&gt;&#x2F;catalog&#x2F;&#x2F;use&lt;&#x2F;code&gt; will also work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;querying-attributes&quot;&gt;Querying Attributes&lt;&#x2F;h2&gt;
&lt;p&gt;To get the attribute of a tag use the notation &lt;code&gt;&#x2F;path&#x2F;to&#x2F;tag&#x2F;@attribute&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; To get the &lt;code&gt;href&lt;&#x2F;code&gt; attributes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F;link&#x2F;@href
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;dealing-with-multiple-elements-using-predicates&quot;&gt;Dealing with Multiple Elements using Predicates&lt;&#x2F;h2&gt;
&lt;p&gt;When specifying an XPath that yields multiple results maybe one wants not all elements that match a tag.&lt;&#x2F;p&gt;
&lt;p&gt;Writing a number in square brackets will work similar to an array access in a programming language: &lt;code&gt;tag[1]&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Select the second &lt;code&gt;item&lt;&#x2F;code&gt; from the catalog:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;catalog&#x2F;item[2]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In place of the Number on can also use expressions like &lt;code&gt;last()&lt;&#x2F;code&gt;, &lt;code&gt;last()-1&lt;&#x2F;code&gt; or &lt;code&gt;position()&amp;gt;1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;One can also compare against attributes here.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Only select the &lt;code&gt;type=&amp;quot;wikipedia&amp;quot;&lt;&#x2F;code&gt; links.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F;link[@type=&amp;#39;wikipedia&amp;#39;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note on Quoting:&lt;&#x2F;b&gt; XPath uses single quotes so one can easily use it in XML attributes.&lt;&#x2F;p&gt;
&lt;p&gt;!note: &lt;b&gt;Boolean Logic:&lt;&#x2F;b&gt; Boolean logic is implemented with the &lt;code&gt;and&lt;&#x2F;code&gt; and &lt;code&gt;or&lt;&#x2F;code&gt; keywords and the &lt;code&gt;not()&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;These are way more possibilities, the &lt;a href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xpath_syntax.asp&quot;&gt;w3schools tutorial has a more complete predicate list&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-node-text&quot;&gt;Getting Node Text&lt;&#x2F;h2&gt;
&lt;p&gt;To get the text inside a node use &lt;code&gt;&#x2F;text()&lt;&#x2F;code&gt; to select it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Get the text from the first items name:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;&#x2F;item[1]&#x2F;name&#x2F;text()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;combine-multiple-xpaths&quot;&gt;Combine multiple XPaths&lt;&#x2F;h2&gt;
&lt;p&gt;Sometimes the result of one XPath isn&#x27;t enough and you want the combined results of multiple XPaths. (Like a SQL union)&lt;&#x2F;p&gt;
&lt;p&gt;To achieve this you join multiple XPaths using a pipe &lt;code&gt;|&lt;&#x2F;code&gt; character like this: &lt;code&gt; &#x2F;xpath1 | &#x2F;xpath1&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xpath-with-namespaces&quot;&gt;XPath with Namespaces&lt;&#x2F;h2&gt;
&lt;p&gt;If some of your Nodes are part of a namespace, i.e. using the &lt;code&gt;&amp;lt;namespace:tag&amp;gt;&lt;&#x2F;code&gt; syntax or the &lt;code&gt;&amp;lt;tag xmlns=&amp;quot;https:&#x2F;&#x2F;example.org&amp;quot;&amp;gt;&lt;&#x2F;code&gt; attribute&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; When using the &lt;code&gt;xmlns&lt;&#x2F;code&gt; attribute all children of that node are also in that namespace unless declared otherwise.&lt;&#x2F;p&gt;
&lt;p&gt;When a namespace is used you&#x27;ll notice that your usual queries don&#x27;t work for some strange reason.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Snippet of Atom-Feed serving as an example documents here.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;feed&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;xmlns&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;2005&#x2F;Atom&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-namespace z-xml&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-namespace z-xml&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;lang&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;en&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;slatecave.net&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-comment z-block z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-xml&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt; Rest of feed &lt;span class=&quot;z-punctuation z-definition z-comment z-end z-xml&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;feed&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;To get the title one may want to try the XPath &lt;code&gt;&#x2F;feed&#x2F;title&lt;&#x2F;code&gt;, but this will fail because of the namespace.&lt;&#x2F;p&gt;
&lt;p&gt;If it is possible to declare the namespace (i.e. in an XSLT-Sheet) then do that and select the element with the namespace prefix, i.e. &lt;code&gt;&#x2F;atom:feed&#x2F;atom:title&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If it is not possible to declare the Namespace you can work around that by using the &lt;code&gt;local-name()&lt;&#x2F;code&gt; function like this: &lt;code&gt;*[local-name()=&#x27;feed&#x27;]&#x2F;*[local-name()=&#x27;title&#x27;]&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;useful-links&quot;&gt;Useful Links&lt;&#x2F;h2&gt;
&lt;p&gt;All of the following lead to the w3schools page.&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xpath_syntax.asp&quot;&gt;XPath Syntax&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xpath_operators.asp&quot;&gt;XPath Operators&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xsl_functions.asp&quot;&gt;XPath Functions&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;xml&#x2F;xpath_intro.asp&quot;&gt;XPath Tutorial&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;playing-with-xpath&quot;&gt;Playing with XPath&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;using-xmllint&quot;&gt;Using &lt;code&gt;xmllint&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;xmllint&lt;&#x2F;code&gt; command line utility can do XPath.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;xmllint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;file.xml&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-terminator z-file-descriptor z-shell&quot;&gt;-&lt;&#x2F;span&gt;-xpath &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&amp;lt;xpath&amp;gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;xmllint&lt;&#x2F;code&gt; and XPath are the &lt;code&gt;jq&lt;&#x2F;code&gt; of XML, if they seem a bit clunky that is because they have been around for a freaking long time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-information-from-html&quot;&gt;Getting information from HTML&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Get the title of the HTML Document.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; https:&#x2F;&#x2F;slatecave.net&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;xmllint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; -&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;html&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;xpath&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&#x2F;html&#x2F;head&#x2F;title&#x2F;text()&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-file-descriptor z-shell&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-file-descriptor z-shell&quot;&gt;-&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; Get the social media preview description.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;curl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; https:&#x2F;&#x2F;slatecave.net&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;xmllint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; -&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;html&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;xpath&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;string(
	&#x2F;html&#x2F;head&#x2F;meta[@property=&amp;#39;og:description&amp;#39;]&#x2F;@content |
	&#x2F;html&#x2F;head&#x2F;meta[@property=&amp;#39;og:description&amp;#39;]&#x2F;text()
	)&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-file-descriptor z-shell&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-file-descriptor z-shell&quot;&gt;-&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Explanation:&lt;&#x2F;b&gt; Both examples use &lt;code&gt;-&lt;&#x2F;code&gt; to tell &lt;code&gt;xmllint&lt;&#x2F;code&gt; to read from its standard input (that is the &lt;code&gt;curl&lt;&#x2F;code&gt; output here), enable the HTML parser with the &lt;code&gt;--html&lt;&#x2F;code&gt; option and discard any errors from &lt;code&gt;xmllint&lt;&#x2F;code&gt; using &lt;code&gt;2&amp;gt;&amp;amp;-&lt;&#x2F;code&gt;, which tells the shell to close the standard error.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Getting your Smartcards and Security Tokens working on Linux</title>
        <published>2023-12-03T00:00:00+00:00</published>
        <updated>2023-12-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/get-security-token-working-on-linux/"/>
        <id>https://slatecave.net/notebook/get-security-token-working-on-linux/</id>
        
        <summary type="text">You just bought a popular Security Token to find it not working out of the box on Linux?</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/get-security-token-working-on-linux/">&lt;p&gt;Most Problems I had with Smartcards or Security Tokens just boiled down to missing packages. (&lt;code&gt;pcsclite&lt;&#x2F;code&gt;, &lt;code&gt;ccid&lt;&#x2F;code&gt; and &lt;code&gt;libfido2&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-are-smartcards-and-security-tokens&quot;&gt;What are Smartcards and Security Tokens?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Security_token&quot;&gt;Security Tokens&lt;&#x2F;a&gt; are small devices that hold cryptographic keys and implement some number of protocols that make those keys usable without them ever leaving the device.&lt;&#x2F;p&gt;
&lt;p&gt;This behaviour is useful because nobody can steal your key from such a device, even if you plug that Security token into a virus dripping publicly accessible PC (though that doesn&#x27;t mean you should).&lt;&#x2F;p&gt;
&lt;p&gt;Popular security Tokens are: &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;YubiKey&quot;&gt;YubiKey&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Nitrokey&quot;&gt;Nitrokey&lt;&#x2F;a&gt;, the &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Tillitis_TKey&quot;&gt;Tillitis TKey&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Solo&quot;&gt;SoloKey&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I&#x27;m not qualified to to recommend any Security Tokens, these are just the popular ones I know people will search for.&lt;&#x2F;p&gt;
&lt;p&gt;Smartcards are the same technology in card form, examples include modern bank cards, passports and other credit card sized plastic lumps with a chip baked into them.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll use the terms &lt;i&gt;smartcards&lt;&#x2F;i&gt; and &lt;i&gt;security token&lt;&#x2F;i&gt; interchangeably here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;install-missing-packages&quot;&gt;Install missing Packages&lt;&#x2F;h2&gt;
&lt;p&gt;For some reason the utilities for using Smartcards or  don&#x27;t declare the needed backends as their dependencies, those backends are needed to turn sometime obscure USB or NFC interfaces into APIs that applications can access without worrying about device details.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pcsclite-and-ccid&quot;&gt;&lt;code&gt;pcsclite&lt;&#x2F;code&gt; and &lt;code&gt;ccid&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Middleware to access a smart card using SCard API (PC&#x2F;SC).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pcsclite.apdu.fr&#x2F;&quot;&gt;pcsclite&lt;&#x2F;a&gt; provides a service that takes PC&#x2F;SC hardware and patches it through to userspace. Usually you need a driver for your Smartcards reader which comes in the &lt;code&gt;ccid&lt;&#x2F;code&gt; (sometimes &lt;code&gt;pcsc-ccid&lt;&#x2F;code&gt;) package. (You may need a more specific driver in some cases)&lt;&#x2F;p&gt;
&lt;p&gt;It is needed for the part of the smartcard that can do actual signing and encryption, and is commonly used with OpenPGP or PKCS#11.&lt;&#x2F;p&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Smartcards&quot;&gt;The ArchWiki on Smartcards&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.debian.org&#x2F;Smartcards&quot;&gt;The Debian Wiki on Smartcards&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h3 id=&quot;libfido2&quot;&gt;&lt;code&gt;libfido2&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;FIDO2_Project&quot;&gt;FIDO2&lt;&#x2F;a&gt; (the successor to &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Universal_2nd_Factor&quot;&gt;Universal 2nd Factor&lt;&#x2F;a&gt;) provides authentication functionality usually used as a second factor to log into websites.&lt;&#x2F;p&gt;
&lt;p&gt;Usually installing the &lt;code&gt;libfido2&lt;&#x2F;code&gt; package solves the problem that your Security Token is not recognised as it also contains all the needed udev tweaks and so on. Maybe a reboot is necessary.&lt;&#x2F;p&gt;
&lt;p&gt;Testing this functionality with your browser can be done using &lt;a href=&quot;https:&#x2F;&#x2F;demo.yubico.com&#x2F;&quot;&gt;Yubicos demo page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;wiki.archlinux.org&amp;#x2F;title&amp;#x2F;Universal_2nd_Factor&quot;&gt;
		The ArchWiki has more information on Universal 2nd Factor and FIDO
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;!note: &lt;b&gt;YubiKey Note:&lt;&#x2F;b&gt; Installing &lt;code&gt;libfido2&lt;&#x2F;code&gt; also fixed the not very helpful &lt;br&gt;&lt;code&gt;WARNING: No OTP HID backend available. OTP protocols will not function&lt;&#x2F;code&gt; &lt;br&gt;for the YubiKey Manager (&lt;code&gt;ykman&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;interesting-things-to-do-with-a-security-token&quot;&gt;Interesting Things to do with a Security Token&lt;&#x2F;h2&gt;
&lt;p&gt;Now that your token is working why not try some interesting things?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Warning:&lt;&#x2F;b&gt; Interesting also means that things can go wrong. Make sure you don&#x27;t lock yourself out of anything important and use a test system if in doubt.&lt;&#x2F;p&gt;
&lt;p&gt;In case you want to use these devices to log into your Linux user or authenticate &lt;code&gt;sudo&lt;&#x2F;code&gt; with one of these Tokens you want to look into PAM Modules. See the &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Universal_2nd_Factor#Authentication_for_user_sessions&quot;&gt;ArchWiki on Authentication for User Sessions with FIDO2&lt;&#x2F;a&gt; for more information.&lt;&#x2F;p&gt;
&lt;p&gt;You can also secure you ssh-keys with these tokens. See the &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;SSH_keys#FIDO&#x2F;U2F&quot;&gt;ArchWiki on ssh with FIDO&#x2F;U2F&lt;&#x2F;a&gt; or the &lt;a href=&quot;https:&#x2F;&#x2F;media.ccc.de&#x2F;v&#x2F;gpn21-28-noch-besser-leben-mit-ssh#t=2116&quot;&gt;Talk &lt;q lang=&quot;de&quot;&gt;Noch besser leben mit SSH&lt;&#x2F;q&gt; (from 35:18)&lt;&#x2F;a&gt; (at the time of writing no English translation is available) held by leyrer at GPN21.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>SQL Cheatsheet</title>
        <published>2023-11-18T00:00:00+00:00</published>
        <updated>2023-11-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/sql/"/>
        <id>https://slatecave.net/notebook/sql/</id>
        
        <summary type="text">Some useful reminders for the occasional SQL user.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/sql/">&lt;h2 id=&quot;sql-query-anatomy&quot;&gt;SQL Query Anatomy&lt;&#x2F;h2&gt;
&lt;p&gt;Most databases don&#x27;t like it when the SQL clauses are out of order.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Operator
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT … FROM …&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_select.html&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;INSERT INTO &amp;lt;table&amp;gt;(…) VALUES (…),…&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_insert.html&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;UPDATE &amp;lt;table&amp;gt; SET …&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_update.html&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DELETE FROM …&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_delete.html&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;JOIN&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;WHERE&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;GROUP BY&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;HAVING&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ORDER BY&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;LIMIT&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;OFFSET&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;null-tricks&quot;&gt;NULL Tricks&lt;&#x2F;h2&gt;
&lt;p&gt;Null in SQL is special as it isn&#x27;t equal (&lt;code&gt;=&lt;&#x2F;code&gt;) to anything, not even itself, which seems wiered when coming from programming languages but makes perfect sense when considering that &lt;code&gt;NULL&lt;&#x2F;code&gt; means &amp;quot;unknown value&amp;quot; wich definitely isn&#x27;t equal to another &amp;quot;unknown value&amp;quot;.&lt;&#x2F;p&gt;
&lt;p&gt;If you want null to behave more like in a programming language use the &lt;code&gt;is&lt;&#x2F;code&gt; keyword, where &lt;code&gt;NULL is NULL&lt;&#x2F;code&gt; evaluates to 1 opposed to &lt;code&gt;NULL = NULL&lt;&#x2F;code&gt; which results in a 0.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; For all other values &lt;code&gt;is&lt;&#x2F;code&gt; acts like &lt;code&gt;=&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To get a default value at the time of a query there is the &lt;code&gt;coalesce()&lt;&#x2F;code&gt; function which takes multiple aruments and returns the first non-null one. (&lt;a href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;lang_corefunc.html#coalesce&quot;&gt;SQLite&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;nulls.html&quot;&gt;SQLite has a page with more null quirks, comparing SQLite to other Database engines.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;join-types&quot;&gt;Join Types&lt;&#x2F;h2&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example SQL &lt;code&gt;JOIN&lt;&#x2F;code&gt; statement.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql z-code&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;z-source z-sql&quot;&gt;&lt;span class=&quot;z-keyword z-other z-DML z-sql&quot;&gt;SELECT&lt;&#x2F;span&gt; …
&lt;span class=&quot;z-keyword z-other z-DML z-sql&quot;&gt;FROM&lt;&#x2F;span&gt; bookmark
&lt;span class=&quot;z-keyword z-other z-DML z-sql&quot;&gt;INNER JOIN&lt;&#x2F;span&gt; url ON &lt;span class=&quot;z-constant z-other z-database-name z-sql&quot;&gt;url&lt;&#x2F;span&gt;.&lt;span class=&quot;z-constant z-other z-table-name z-sql&quot;&gt;url_id&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-comparison z-sql&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-other z-database-name z-sql&quot;&gt;bookmark&lt;&#x2F;span&gt;.&lt;span class=&quot;z-constant z-other z-table-name z-sql&quot;&gt;url_id&lt;&#x2F;span&gt;
…
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;What all joins have in common is that they glue the tables together and output the entries where the &lt;code&gt;ON&lt;&#x2F;code&gt; condition is true.&lt;&#x2F;p&gt;
&lt;p&gt;They differ in how they treat entries that don&#x27;t make it into the output because they didn&#x27;t find a &amp;quot;partner&amp;quot; using the &lt;code&gt;ON&lt;&#x2F;code&gt; condition.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;inner&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		only returns entries that found a partner.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;left&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		doesn&#x27;t drop columns from the already present columns, leaving the joined columns &lt;code&gt;NULL&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;right&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		additionally outputs all entries from the joined table that didn&#x27;t find a partner, leaving the other columns &lt;code&gt;NULL&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;full&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		like a &lt;code&gt;left&lt;&#x2F;code&gt; and &lt;code&gt;right&lt;&#x2F;code&gt; join, returning all of the entries in both tables, joining the ones that are join-able and filling in where joining isn&#x27;t possible.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;join-examples&quot;&gt;Join Examples&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; To keep this example as clean as possible types have been omitted, the SQL is intended to be compatible with &lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&quot;&gt;sqlite3&lt;&#x2F;a&gt;. Use the command &lt;code&gt;sqlite3 :memory:&lt;&#x2F;code&gt; to get an in memory database to play around with.&lt;&#x2F;p&gt;
&lt;p&gt;Assume we have two tables, called &lt;code&gt;left_t&lt;&#x2F;code&gt; and &lt;code&gt;right_t&lt;&#x2F;code&gt; for illustration purposes, bot with a single column.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql z-code&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;z-source z-sql&quot;&gt;&lt;span class=&quot;z-meta z-create z-sql&quot;&gt;&lt;span class=&quot;z-keyword z-other z-create z-sql&quot;&gt;CREATE&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-other z-sql&quot;&gt;TABLE&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-toc-list z-full-identifier z-sql&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-sql&quot;&gt;left_t&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;(lc);
&lt;span class=&quot;z-meta z-create z-sql&quot;&gt;&lt;span class=&quot;z-keyword z-other z-create z-sql&quot;&gt;CREATE&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-other z-sql&quot;&gt;TABLE&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-toc-list z-full-identifier z-sql&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-sql&quot;&gt;right_t&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;(rc);
&lt;span class=&quot;z-keyword z-other z-DML z-sql&quot;&gt;INSERT INTO&lt;&#x2F;span&gt; left_t &lt;span class=&quot;z-keyword z-other z-DML z-II z-sql&quot;&gt;VALUES&lt;&#x2F;span&gt; (&lt;span class=&quot;z-string z-quoted z-single z-sql&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-sql&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;both&lt;span class=&quot;z-punctuation z-definition z-string z-end z-sql&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;),(&lt;span class=&quot;z-string z-quoted z-single z-sql&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-sql&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;left&lt;span class=&quot;z-punctuation z-definition z-string z-end z-sql&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;);
&lt;span class=&quot;z-keyword z-other z-DML z-sql&quot;&gt;INSERT INTO&lt;&#x2F;span&gt; right_t &lt;span class=&quot;z-keyword z-other z-DML z-II z-sql&quot;&gt;VALUES&lt;&#x2F;span&gt; (&lt;span class=&quot;z-string z-quoted z-single z-sql&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-sql&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;both&lt;span class=&quot;z-punctuation z-definition z-string z-end z-sql&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;),(&lt;span class=&quot;z-string z-quoted z-single z-sql&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-sql&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;right&lt;span class=&quot;z-punctuation z-definition z-string z-end z-sql&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can query them an see the results:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql z-code&quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;z-source z-sql&quot;&gt;&lt;span class=&quot;z-keyword z-other z-DML z-sql&quot;&gt;SELECT&lt;&#x2F;span&gt; lc,rc
&lt;span class=&quot;z-keyword z-other z-DML z-sql&quot;&gt;FROM&lt;&#x2F;span&gt; left_t
&lt;span class=&quot;z-keyword z-other z-DML z-sql&quot;&gt;INNER JOIN&lt;&#x2F;span&gt; right_t ON lc &lt;span class=&quot;z-keyword z-operator z-comparison z-sql&quot;&gt;=&lt;&#x2F;span&gt; rc ;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We now performed an inner join of &lt;code&gt;right_t&lt;&#x2F;code&gt; against &lt;code&gt;left_t&lt;&#x2F;code&gt; on the single columns having the same value. The returned record is &lt;code&gt;both|both&lt;&#x2F;code&gt;. Pretty intuitive.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Now we substitute the &lt;code&gt;INNER&lt;&#x2F;code&gt; for a &lt;code&gt;LEFT&lt;&#x2F;code&gt; and the result is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;both|both
left|
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All rows in &lt;code&gt;left_t&lt;&#x2F;code&gt; table mad it through the join, the hole in the &lt;code&gt;right_t&lt;&#x2F;code&gt; was filled with a (blank) &lt;code&gt;NULL&lt;&#x2F;code&gt; field.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If the &lt;code&gt;right_t&lt;&#x2F;code&gt; table had more columns all of them would be returned as &lt;code&gt;NULL&lt;&#x2F;code&gt;, independent of any &lt;code&gt;DEFAULT&lt;&#x2F;code&gt; or &lt;code&gt;NOT NULL&lt;&#x2F;code&gt; statements.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Similar results for a &lt;code&gt;RIGHT&lt;&#x2F;code&gt; join:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;both|both
|right
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now all rows from the &lt;code&gt;right_t&lt;&#x2F;code&gt; table made it through and the missing rows in the &lt;code&gt;left_t&lt;&#x2F;code&gt; table was filled with a &lt;code&gt;NULL&lt;&#x2F;code&gt; entry.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;And for the &lt;code&gt;FULL&lt;&#x2F;code&gt; join:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;both|both
left|
|right
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All rows preserved, the ones that matched got joined that ones without matches had the resulting holes filled with &lt;code&gt;NULL&lt;&#x2F;code&gt; fields.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;database-exploration-commands&quot;&gt;Database Exploration Commands&lt;&#x2F;h2&gt;
&lt;p&gt;You Probably know the problem, a Database you are not familiar with with an engine you don&#x27;t use every day and what were the commands again?&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;caption&gt;Common Tasks and the Corresponding &quot;SQL&quot; Commands.&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Task&lt;&#x2F;th&gt;&lt;th&gt;MariaDB&lt;&#x2F;th&gt;&lt;th&gt;Postgres&lt;&#x2F;th&gt;&lt;th&gt;SQLite&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Get Help&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;help&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\h&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.help&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;List Data&amp;shy;bases&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;show databases&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\l&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.databases&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Use a Data&amp;shy;base&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;use &amp;lt;data&amp;shy;base&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\c &amp;lt;data&amp;shy;base&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;Not available&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;List Tables&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;show tables&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\dt&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.tables&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Describe Table&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;describe &amp;lt;table&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\d &amp;lt;table&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.schema &amp;lt;table&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;Quit Shell&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;exit&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;\q&lt;&#x2F;code&gt;, &lt;code&gt;quit&lt;&#x2F;code&gt;, &lt;code&gt;exit&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;td&gt;&lt;code&gt;.q&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;
&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Git Cheatsheet(s)</title>
        <published>2023-11-03T00:00:00+00:00</published>
        <updated>2023-11-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/git/"/>
        <id>https://slatecave.net/notebook/git/</id>
        
        <summary type="text">Some of my git knowledge and where it came from.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/git/">&lt;h2 id=&quot;setting-up&quot;&gt;Setting Up&lt;&#x2F;h2&gt;
&lt;p&gt;Before starting to use git one should take the few minutes to set it up.&lt;&#x2F;p&gt;
&lt;p&gt;Set up your personal information that will end up on your commits:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; config&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;global&lt;&#x2F;span&gt; user.email &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;your-mail@example.org&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; config&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;global&lt;&#x2F;span&gt; user.name &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&amp;lt;Name&amp;gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Set the default branch name to main instead of master
The reasons I do this are because there was some controversy
around the word &amp;quot;master&amp;quot; in this particular context
and and I quickly realised that &amp;quot;main&amp;quot; is way easier to type.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; config&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;global&lt;&#x2F;span&gt; init.defaultBranch main&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You could set your editor for git commit messages (and other git features) using:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; config&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;global&lt;&#x2F;span&gt; core.editor &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;editor-command&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you (like me) have one editor you want to use for everything: Set the &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;git&#x2F;.&#x2F;env#linux-and-poix&quot;&gt;&lt;code&gt;EDITOR&lt;&#x2F;code&gt; variable in your &lt;code&gt;~&#x2F;.profile&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and git along with a whole range of other programs will pick up on that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;partial-adding&quot;&gt;Partial Adding&lt;&#x2F;h2&gt;
&lt;p&gt;Situation: You have made changes to one file but they should be in two different commits.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; add&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;p&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-control z-regexp z-set z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;&amp;lt;file&amp;gt;&lt;span class=&quot;z-keyword z-control z-regexp z-set z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;git will then chop up the changes into &lt;i&gt;hunks&lt;&#x2F;i&gt; and ask interactively if you want to add&#x2F;stage (&lt;code&gt;y&lt;&#x2F;code&gt;) a change, ignore&#x2F;not stage it (&lt;code&gt;n&lt;&#x2F;code&gt;), further subdivide (&lt;code&gt;s&lt;&#x2F;code&gt;) or even edit (&lt;code&gt;e&lt;&#x2F;code&gt;) it. You can use &lt;code&gt;?&lt;&#x2F;code&gt; to get summary of all options currently available.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;undoing-things&quot;&gt;Undoing Things&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;undo-last-commit&quot;&gt;Undo last Commit&lt;&#x2F;h3&gt;
&lt;p&gt;Situation: You just made a git commit but you committed too much by accident.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; If you just messed up the commit message or need to add changes use &lt;code&gt;git commit --amend&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git reset&lt;&#x2F;code&gt; is your friend here, without any arguments it helps when you &lt;code&gt;git add&lt;&#x2F;code&gt;ed too much, it makes git go into the state it was in at the last commit.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; reset HEAD^1&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;book&#x2F;en&#x2F;v2&#x2F;Git-Tools-Revision-Selection#_ancestry_references&quot;&gt;&lt;code&gt;HEAD^1&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; resets git to the parent commit of the last commit. Effectively acting as if the last commit never happened. (You can still recover it using the revlog)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git reset&lt;&#x2F;code&gt; will only change your git history, not the files you have currently checked out so when using it as described above you won&#x27;t loose any changes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Warning:&lt;&#x2F;b&gt; &lt;code&gt;git reset --hard&lt;&#x2F;code&gt; is intended to throw changes away and it &lt;em&gt;is&lt;&#x2F;em&gt; possible to loose uncommitted data when using it with the &lt;code&gt;--hard&lt;&#x2F;code&gt; flag enabled.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;restore-file-from-last-commit&quot;&gt;Restore File from last Commit&lt;&#x2F;h3&gt;
&lt;p&gt;Situation: You messed up a file and want to throw away the changes since the last commit.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; restore &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;filename&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is less destructive than &lt;code&gt;git reset --hard&lt;&#x2F;code&gt; which throws away all your changes in a repository since the last commit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-peoples-git-guides&quot;&gt;Other Peoples Git Guides&lt;&#x2F;h2&gt;
&lt;p&gt;These are guides made by other people that I think are useful.&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;drewdevault.com&#x2F;2020&#x2F;04&#x2F;06&#x2F;My-weird-branchless-git-workflow.html&quot;&gt;My unorthodox, branchless git workflow&lt;&#x2F;a&gt; Drew DeVault explains how to make use of the fact that the git history of a repository isn&#x27;t set in stone, I mainly use these tricks to completely avoid a lot of merge conflicts in my personal projects.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git-rebase.io&#x2F;&quot;&gt;Learn to change history with git rebase!&lt;&#x2F;a&gt; is Drew DeVault&#x27;s cheatsheet for altering git history in general and the rebase based workflow in the blogpost liked above.&lt;&#x2F;p&gt;
&lt;p&gt;In [Dealing with diverged git branches] Julia Evans explains how to deal with the situation that changes happend on both the local and remote git repository. (She explains it better than I do here.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;media.ccc.de&#x2F;v&#x2F;DiVOC-5-ein_kurzer_blick_in_git#t=0&quot;&gt;Ein kurzer Blick in .git&#x2F;&lt;&#x2F;a&gt; is a talk held at DiVOC in 2020 by Florian Hars diving into the &lt;code&gt;.git&lt;&#x2F;code&gt; directory, unfortunately no one has translated it to English yet.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;jvns.ca&#x2F;blog&#x2F;2024&#x2F;01&#x2F;26&#x2F;inside-git&#x2F;&quot;&gt;Inside .git&lt;&#x2F;a&gt; is a blogpost by Julia Evans that also dives into the &lt;code&gt;.git&lt;&#x2F;code&gt; directory and explains what is inside.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>xdg-mime: Mapping Files to Applications taken apart</title>
        <published>2023-10-03T00:00:00+00:00</published>
        <updated>2023-10-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/xdg-mime/"/>
        <id>https://slatecave.net/blog/xdg-mime/</id>
        
        <summary type="text">How the machinery mapping files to applications works on your free desktop</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/xdg-mime/">&lt;p&gt;last time we took apart &lt;code&gt;xdg-open&lt;&#x2F;code&gt;, the freedesktop file-opener and found out that &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; provides the mapping from file to application. So let&#x27;s take it apart and see what&#x27;s inside …&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-can-xdg-mime-do&quot;&gt;What can xdg-mime do?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;xdg-mime&lt;&#x2F;code&gt; is a shell-script that helps with mapping files to the applications that open them. It implements querying a files mime-type, setting and getting the default application for a given mime-type as well as installing and uninstalling mime-type descriptions.&lt;&#x2F;p&gt;
&lt;p&gt;The code is developed in &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&quot;&gt;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can use &lt;code&gt;xdg-mime --help&lt;&#x2F;code&gt; or &lt;code&gt;man xdg-mime&lt;&#x2F;code&gt; yourself to get more details.&lt;&#x2F;p&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime query filetype &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This sub-command prints the detected mime-type of a given file. (i.e. &lt;code&gt;text&#x2F;plain&lt;&#x2F;code&gt;)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime query default &amp;lt;mimetype&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This prints out the name of the desktop file that is configured as the default opener for the given mime-type. (i.e. &lt;code&gt;slatectx-app-kakoune.desktop&lt;&#x2F;code&gt;)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime default &amp;lt;applications&amp;gt; &amp;lt;mimetype&amp;gt; ...&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Attempts to set the default opener for the one or more given mime-types to the given application which is identified by the name of its desktop file.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime install mimetypes-file &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Installs a mime-type description file that follows the &lt;a href=&quot;https:&#x2F;&#x2F;specifications.freedesktop.org&#x2F;shared-mime-info-spec&#x2F;shared-mime-info-spec-latest.html&quot;&gt;freedesktop mime associations specification&lt;&#x2F;a&gt; where it can be used to identify files. Can be put in &lt;code&gt;user&lt;&#x2F;code&gt; or &lt;code&gt;system&lt;&#x2F;code&gt; mode using the &lt;code&gt;--mode&lt;&#x2F;code&gt; flag.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xdg-mime uninstall mimetypes-file &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		As it says on the tin: It uninstalls a previously installed mime-type file.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; This article ended up being quite long, since it should still be understandable when read in a non-linear manner feel free to skip sections that don&#x27;t seem interesting.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-in-the-script&quot;&gt;What is in the Script?&lt;&#x2F;h2&gt;
&lt;p&gt;Like the other xdg-utils &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; - in addition to a lot of custom logic - imports functionality from the &lt;code&gt;xdg-utils-common.in&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Noteworthy imports from the common file are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a way to detect which desktop one is currently using&lt;&#x2F;li&gt;
&lt;li&gt;mapping the desktop file to the binary it calls and vice versa&lt;&#x2F;li&gt;
&lt;li&gt;helpers for command-line and UI&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Unique to &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a callback to update desktop specific mime database caches.&lt;&#x2F;li&gt;
&lt;li&gt;querying and setting of default applications from gnome, KDE and generic desktops&lt;&#x2F;li&gt;
&lt;li&gt;a function for getting desktop files matching a given mime-type.&lt;&#x2F;li&gt;
&lt;li&gt;functionality for figuring out an application when no explicit default has been set&lt;&#x2F;li&gt;
&lt;li&gt;functionality for opening the default application for KDE, gnome and generic desktops&lt;&#x2F;li&gt;
&lt;li&gt;command line parsing and validating&lt;&#x2F;li&gt;
&lt;li&gt;XML parsing in awk … okay&lt;&#x2F;li&gt;
&lt;li&gt;lots of parsing and updating files with awk&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So this script seems to mostly do the work of parsing and writing the mime to application mapping database, which is to be expected, at first glance mime-type detection seems to be delegated to another command and is in here to provide a stable interface that applications can rely on while &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; does the figuring out how to derive the mime-type.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;xdg-mime&lt;&#x2F;code&gt; integrates with the KDE and Gnome mime databases, because those have been there before &lt;a href=&quot;https:&#x2F;&#x2F;freedesktop.org&quot;&gt;freedesktop.org&lt;&#x2F;a&gt; and the xdg-utils were a thing. More recently Lxqt made their own tool called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lxqt&#x2F;qtxdg-tools&quot;&gt;&lt;code&gt;qtxdg-mat&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; that has been &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;merge_requests&#x2F;48&quot;&gt;integrated into xdg-utils&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;actions-and-cli-plumbing&quot;&gt;Actions and CLI Plumbing&lt;&#x2F;h3&gt;
&lt;p&gt;Since calling &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; is based on sub-commands, some with their own custom options, this results the parser being a lot of nested &lt;code&gt;case&lt;&#x2F;code&gt; statements and loops for parsing the extra options and arguments.&lt;&#x2F;p&gt;
&lt;p&gt;To keep the logic from becoming an indentation mess &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; has a concept of actions. That is, that the command gets translated into an action-name that is written to the &lt;code&gt;action&lt;&#x2F;code&gt; variable. Which is then reused for deciding what to do.&lt;&#x2F;p&gt;
&lt;p&gt;Potential values for the &lt;code&gt;action&lt;&#x2F;code&gt; variable are:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;info&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for the filename to mime-type query. with the &lt;code&gt;xdg-mime query filetype&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;makedefault&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for setting default mime-handlers with the &lt;code&gt;xdg-mime default&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;defapp&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for querying the default application with the &lt;code&gt;xdg-mime query default&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;install&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for installing mime descriptions with the &lt;code&gt;xdg-mime install&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;uninstall&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for uninstalling mime descriptions with the &lt;code&gt;xdg-mime uninstall&lt;&#x2F;code&gt; sub-command.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;getting-the-mimetype-of-a-file&quot;&gt;Getting the MimeType of a File&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;info&lt;&#x2F;code&gt; action triggers a desktop detection and then decides which mime-type detection method to use. On success the output must be the mime-type of the file followed by a newline without any extra information.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If the desktop is KDE and &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;file&lt;&#x2F;code&gt; is not an executable use &lt;code&gt;info_kde&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If the desktop belongs to the gnome gio family (Gnome,Cinnamon,Lxde,Mate,Xfce) use &lt;code&gt;info_gnome&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;In case of Lxqt use &lt;code&gt;info_lxqt&lt;&#x2F;code&gt; (new in the beta 1.2.0 release)&lt;&#x2F;li&gt;
&lt;li&gt;Otherwise use &lt;code&gt;info_generic&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;lxqt-mimetype-query&quot;&gt;Lxqt MimeType Query&lt;&#x2F;h3&gt;
&lt;p&gt;For Lxqt &lt;code&gt;qtxdg-mat mimetype &amp;lt;file&amp;gt;&lt;&#x2F;code&gt; is used to query the mime information of a given file.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kde-mimetype-query&quot;&gt;KDE MimeType Query&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;info_kde&lt;&#x2F;code&gt; checks the &lt;code&gt;KDE_SESSION_VERSION&lt;&#x2F;code&gt; environment variable.&lt;&#x2F;p&gt;
&lt;p&gt;For KDE 4 and 6 it uses the &lt;code&gt;kmimetypefinder&lt;&#x2F;code&gt; command, for KDE 5 &lt;code&gt;kmimetypefinder5&lt;&#x2F;code&gt; and for older versions it gets the mime-type from &lt;code&gt;kfile&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Extra information is cut away using POSIX tools.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gnome-mimetype-query&quot;&gt;Gnome MimeType Query&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;info_gnome&lt;&#x2F;code&gt; uses a mechanism similar to file opening where it uses a fallback cascade of multiple methods for detecting the mime type, using the first one that is available.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;gio info &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gvfs-info &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gnomevfs-info --slow-mime &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Like with the KDE method any extra information is discarded using standard POSIX tools.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generic-mimetype-query&quot;&gt;Generic MimeType query&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;info_generic&lt;&#x2F;code&gt; also uses a little fallback cascade.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;mimetype --brief --dereference &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;file --brief --dereference --mime-type &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For both commands cutting away extra information is not needed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;file&lt;&#x2F;code&gt;:&lt;&#x2F;b&gt; It seems strange that &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;file&lt;&#x2F;code&gt; is used here instead of just calling the &lt;code&gt;file&lt;&#x2F;code&gt; command. After finding no information on it and asking the original developers (&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;commit&#x2F;76e2c9cf157606f73b297f9a3dbd53125fc82510#286a7a0cd84d236c319bbc342526456a380e56a0_0_51&quot;&gt;this quirk has been around since 2006&lt;&#x2F;a&gt;) it turns out that nobody knows why the absolute path was used here, it would most probably not make a difference to change it back.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-xdg-mime-figures-out-the-default-application&quot;&gt;How xdg-mime figures out the Default Application&lt;&#x2F;h2&gt;
&lt;p&gt;This functionality hides behind the &lt;code&gt;defapp&lt;&#x2F;code&gt; action and can be triggered using the &lt;code&gt;xdg-mime query default &amp;lt;mimetype&amp;gt;&lt;&#x2F;code&gt; command. As a reminder this returns the name of the desktop-file describing the application configured as the default opener or an application that seems the best fit according to some simple heuristics in case no default is was explicitly set.&lt;&#x2F;p&gt;
&lt;p&gt;This time there are only three implementations, a generic one, a new one for Lxqt and one for KDE below version 6.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;issues&#x2F;147&quot;&gt;application returned by &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; is not necessarily the one that will open when calling &lt;code&gt;xdg-open&lt;&#x2F;code&gt; on the same file&lt;&#x2F;a&gt;, mostly because &lt;code&gt;xdg-open&lt;&#x2F;code&gt; delegates to openers which can have custom logic.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kde-default-application-querying&quot;&gt;KDE Default Application Querying&lt;&#x2F;h3&gt;
&lt;p&gt;KDE has a subsystem called &lt;a href=&quot;https:&#x2F;&#x2F;techbase.kde.org&#x2F;Development&#x2F;Tutorials&#x2F;Services&#x2F;Traders&quot;&gt;Trader&lt;&#x2F;a&gt; which handles delegation of actions between various KDE components (&lt;i&gt;services&lt;&#x2F;i&gt;). To use it &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; invokes &lt;code&gt;ktraderclient&lt;&#x2F;code&gt;, &lt;code&gt;ktraderclient5&lt;&#x2F;code&gt; or &lt;code&gt;ktradertest&lt;&#x2F;code&gt; (for older than KDE 4) as &lt;code&gt;$KTRADER&lt;&#x2F;code&gt;. Asking it for a matching &lt;code&gt;Application&lt;&#x2F;code&gt; &lt;i&gt;service&lt;&#x2F;i&gt; for the given mime-type, extracting the result from the returned text.&lt;&#x2F;p&gt;
&lt;p&gt;If no ktrader command appropriate to the current KDE version can be found it falls back to the generic implementation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;lxqt-default-application-querying&quot;&gt;Lxqt Default Application Querying&lt;&#x2F;h3&gt;
&lt;p&gt;Again &lt;code&gt;qtxdg-mat&lt;&#x2F;code&gt; is used:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;qtxdg-mat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; defapp &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;file&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;generic-default-application-querying&quot;&gt;Generic Default Application Querying&lt;&#x2F;h3&gt;
&lt;p&gt;For where there is no desktop specific mechanism, &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; has a generic, specification compliant 🥳 way to get the default application using the &lt;code&gt;defapp_generic&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;desktop-prefixes&quot;&gt;Desktop Prefixes&lt;&#x2F;h4&gt;
&lt;p&gt;Before getting into querying default applications, first imagine someone switches between multiple desktops, maybe &lt;code&gt;mate&lt;&#x2F;code&gt; and &lt;code&gt;kde&lt;&#x2F;code&gt;, while you ave some applications you want to be the default everywhere (i.e. you preferred web-browser) other preferences might depend on the desktop you are currently using like the image- or PDF-viewer.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of the desktops stepping on each others toes, every desktop that identifies itself via the &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt; environment variable gets its own configuration file, prefixed by the lowercased name that is read from that variable. This way different preferences on different desktops can be kept separate.&lt;&#x2F;p&gt;
&lt;p&gt;This prefix will be referred to as &lt;code&gt;${prefix}&lt;&#x2F;code&gt; further down. Examples of its contents may be: &lt;code&gt;mate-&lt;&#x2F;code&gt;, &lt;code&gt;kde-&lt;&#x2F;code&gt;, etc. or it may be empty.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt; works completely independent of the desktop detection (in fact: the desktop detection uses it). So anyone can set this to get their own configuration space for their fresh new custom desktop.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt;, like many other XDG variables is a colon separated list that is handled with a fallback cascade. i.e. if your custom &lt;code&gt;xfce&lt;&#x2F;code&gt; based rice identifies itself as &lt;code&gt;my-xfce-rice:xfce&lt;&#x2F;code&gt;, then the application choice will be pulled from &lt;code&gt;my-xfce-rice&lt;&#x2F;code&gt; and automatically fall back to the &lt;code&gt;xfce&lt;&#x2F;code&gt; defaults, even when other desktops are installed.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;reading-the-association-files&quot;&gt;Reading the Association Files&lt;&#x2F;h4&gt;
&lt;p&gt;This is a multistage search that …&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;checks the users &lt;code&gt;$XDG_CONFIG_HOME&#x2F;${prefix}mimeapps.list&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;checks the system wide &lt;code&gt;$XDG_CONFIG_DIRS&#x2F;${prefix}mimeapps.list&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;checks again for an &lt;code&gt;applications&#x2F;${prefix}mimeapps.list&lt;&#x2F;code&gt; in both system and user data directories (usually &lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;searches both user and system data directories for a prefixed with the current desktops name &lt;code&gt;applications&#x2F;${prefix}defaults.list&lt;&#x2F;code&gt; and &lt;code&gt;applications&#x2F;${prefix}mimeinfo.cache&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;falls back to searching the application files for the one that sees itself most fit for opening a file with the given mime-type using the &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-mime&#x2F;#fallback-application-finding&quot;&gt;&lt;code&gt;defapp_fallback&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The files are searched in the order of highest priority to lowest and when the first result is found it is returned.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;While the filenames are different those files share the same ini syntax.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;ini&quot; class=&quot;language-ini z-code&quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;z-source z-genconfig&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;mimtype&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;desktop&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;-&lt;&#x2F;span&gt;file&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;-&lt;&#x2F;span&gt;name&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;[;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;another&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;-&lt;&#x2F;span&gt;desktop&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;-&lt;&#x2F;span&gt;file&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;-&lt;&#x2F;span&gt;name&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;.&lt;&#x2F;span&gt;][;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;For the &lt;code&gt;mimeapps.list&lt;&#x2F;code&gt; files, the &lt;code&gt;check_mimeapps_list&lt;&#x2F;code&gt; function searches the &lt;code&gt;[Default Applications]&lt;&#x2F;code&gt; section for an entry with the given mime-type. If such an entry exists, all mentioned desktop-file names are extracted and the first one with a valid executable is returned. If not the search continues.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Bug Note:&lt;&#x2F;b&gt; Apparently there is a bug in &lt;code&gt;check_mimeapps_list&lt;&#x2F;code&gt; that &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;merge_requests&#x2F;53&quot;&gt;discards any names after the first semicolon&lt;&#x2F;a&gt;, making the loop that should handle fallback useless.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;defaults.list&lt;&#x2F;code&gt; and &lt;code&gt;mimeinfo.cache&lt;&#x2F;code&gt; get treated simpler than the &lt;code&gt;mimeapps.list&lt;&#x2F;code&gt; files. The first result that mates the mime-type is returned, done. Sections are ignored for these files.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;fallback-application-finding&quot;&gt;Fallback Application Finding&lt;&#x2F;h4&gt;
&lt;p&gt;In case no desktop application is found using the configuration file &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; will start searching one itself with the &lt;code&gt;defapp_fallback&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;This iterates over all desktop files that match the given mime-type, picking the one with the highest value in &lt;code&gt;InitialPreference&lt;&#x2F;code&gt; (default is 0 when unset).
In case multiple applications have the same &lt;code&gt;InitialPreference&lt;&#x2F;code&gt;, the one first found or in the higher priority location wins.&lt;&#x2F;p&gt;
&lt;p&gt;If a file is found this way it gets returned.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; For the &lt;code&gt;InitialPreference&lt;&#x2F;code&gt;, the &lt;a href=&quot;https:&#x2F;&#x2F;specifications.freedesktop.org&#x2F;desktop-entry-spec&#x2F;desktop-entry-spec-1.1.html#kde-items&quot;&gt;desktop file specification only says that it is reserved for KDE&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-default-applications&quot;&gt;Setting Default Applications&lt;&#x2F;h2&gt;
&lt;p&gt;Setting a default application for a given mime-type happens in one of two ways:&lt;&#x2F;p&gt;
&lt;p&gt;For Lxqt only the &lt;code&gt;make_default_lxqt&lt;&#x2F;code&gt; function gets called.&lt;&#x2F;p&gt;
&lt;p&gt;For everything else there are two functions, the &lt;code&gt;make_default_kde&lt;&#x2F;code&gt; one where the KDE database gets updated and &lt;code&gt;make_default_generic&lt;&#x2F;code&gt; which updates the users &lt;code&gt;mimeapps.list&lt;&#x2F;code&gt; file.
Weirdly both are always called. (&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-mime&#x2F;#desktop-prefixes&quot;&gt;Remember the whole prefix thing?&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generic-default-application-setting&quot;&gt;Generic Default Application Setting&lt;&#x2F;h3&gt;
&lt;p&gt;Surprisingly unsurprising the generic case is the simpler one here.&lt;&#x2F;p&gt;
&lt;p&gt;It derives the path for &lt;code&gt;$XDG_CONFIG_HOME&#x2F;mimeapps.list&lt;&#x2F;code&gt; and then runs an awk script on it. This script adds or updates the &lt;code&gt;&amp;lt;mimetype&amp;gt;=&amp;lt;desktop-file-name&amp;gt;;&lt;&#x2F;code&gt; association entry in the &lt;code&gt;[Default Applications]&lt;&#x2F;code&gt; section that belongs to the given mime-type.&lt;&#x2F;p&gt;
&lt;p&gt;What is a bit surprising is that the desktop prefix isn&#x27;t handled here.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;for-lxqt&quot;&gt;For Lxqt&lt;&#x2F;h3&gt;
&lt;p&gt;Again &lt;code&gt;qtxdg-mat&lt;&#x2F;code&gt; has a sub-command that is a prefect fit and doesn&#x27;t need any wrapping:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;qtxdg-mat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; defapp&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;set&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;file&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;mimetype&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;for-kde-3-and-4&quot;&gt;For KDE 3 and 4&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;make_default_kde&lt;&#x2F;code&gt; function is a lot of awk code that seems to do patching on two files.
Unfortunately it &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;issues&#x2F;12&quot;&gt;hasn&#x27;t been updated after KDE 4&lt;&#x2F;a&gt; …&lt;&#x2F;p&gt;
&lt;h4 id=&quot;kde-4&quot;&gt;KDE 4&lt;&#x2F;h4&gt;
&lt;p&gt;The KDE 4 specific one modifies a &lt;code&gt;mimeapps.list&lt;&#x2F;code&gt; file in a directory returned by &lt;code&gt;kde4-config --path xdgdata-apps 2&amp;gt; &#x2F;dev&#x2F;null | cut -d &#x27;:&#x27; -f 1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It adds the mime-type to application mapping to the &lt;code&gt;[Added Associations]&lt;&#x2F;code&gt; section if it is missing.&lt;&#x2F;p&gt;
&lt;p&gt;My personal guess without doing further research is that this ensures that the application to mime-type mapping exists because something in KDE actually validates that an application can open a given mime-type. I&#x27;m also pretty sure that this mechanism relies on the generic setter running immediately after for setting the entry in the &lt;code&gt;[Default Applications]&lt;&#x2F;code&gt; section. Maybe someone can confirm or deny this.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;kde-3&quot;&gt;KDE 3&lt;&#x2F;h4&gt;
&lt;p&gt;For KDE 3 (the default case if the version isn&#x27;t 4), the &lt;code&gt;profilerc&lt;&#x2F;code&gt; file in a directory returned by &lt;code&gt;kde-config --path config 2&amp;gt; &#x2F;dev&#x2F;null | cut -d &#x27;:&#x27; -f 1&lt;&#x2F;code&gt; gets modified.&lt;&#x2F;p&gt;
&lt;p&gt;There with ini-like syntax, a section gets added:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ini&quot; class=&quot;language-ini z-code&quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;z-source z-genconfig&quot;&gt;&lt;span class=&quot;z-storage z-type z-genconfig&quot;&gt;[&amp;lt;mimetype&amp;gt; - 1]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;Application&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;desktop&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;-&lt;&#x2F;span&gt;file&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;-&lt;&#x2F;span&gt;name&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;AllowAsDefault&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language z-genconfig&quot;&gt;true&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;GenericServiceType&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Application
&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;Preference&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-genconfig&quot;&gt;1&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;ServiceType&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;mimetype&lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Sections that act as duplicates will get removed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installing-and-uninstalling-mimetype-descriptions&quot;&gt;Installing and Uninstalling MimeType Descriptions&lt;&#x2F;h2&gt;
&lt;p&gt;Now that everything from getting the mime-type to the application is covered one may ask:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;How does the mime detection tooling know about all the file-types out there?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;On a freedesktop system this information resides in the &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Specifications&#x2F;shared-mime-info-spec&#x2F;&quot;&gt;XDG Shared MIME-info Database&lt;&#x2F;a&gt; which is a binary compiled from XML files that describe how to detect a given mime-type, and what its icon, names and descriptions (in different languages) are.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;xdg-mime&lt;&#x2F;code&gt; makes it easier to install those XML descriptions. (When packaging follow the target distributions guidelines first!)&lt;&#x2F;p&gt;
&lt;p&gt;For the general case installing puts&#x2F;deletes the given file in the desired &lt;code&gt;mime&#x2F;packages&lt;&#x2F;code&gt; directory.&lt;&#x2F;p&gt;
&lt;p&gt;For user mode that is &lt;code&gt;$XDG_DATA_HOME&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt;, defaulting to &lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt;. For the system mode &lt;code&gt;$XDG_DATA_DIRS&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt;, defaulting to &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;share&#x2F;mime&#x2F;packages&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a fallback for a KDE3 mechanism called &amp;quot;mimelink&amp;quot; but I will skip that one for now.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Don&#x27;t confuse the files in the &lt;code&gt;packages&lt;&#x2F;code&gt; directory with other XML files named like the mime-types themselves. The ones in &lt;code&gt;packages&lt;&#x2F;code&gt; really are packages containing the descriptions for multiple mime-types while the other ones are already unpacked for better discovery.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hooks-on-the-mime-database&quot;&gt;Hooks on the Mime Database&lt;&#x2F;h3&gt;
&lt;p&gt;After (un)installing the &lt;code&gt;update_mime_database&lt;&#x2F;code&gt; function takes care of making sure that the mime database and caches get rebuilt.&lt;&#x2F;p&gt;
&lt;p&gt;When in user mode, the currently running desktop is KDE and a display is present &lt;code&gt;kbuildsycoca&lt;&#x2F;code&gt;, &lt;code&gt;kbuildsycoca4&lt;&#x2F;code&gt; or &lt;code&gt;kbuildsycoca5&lt;&#x2F;code&gt; is called, which rebuilds KDE configuration caches.&lt;&#x2F;p&gt;
&lt;p&gt;After that every directory in &lt;code&gt;$PATH&lt;&#x2F;code&gt; with the addition of &lt;code&gt;&#x2F;opt&#x2F;gnome&#x2F;bin&lt;&#x2F;code&gt; is searched for a &lt;code&gt;update-mime-database&lt;&#x2F;code&gt; program. &lt;em&gt;All&lt;&#x2F;em&gt; found binaries are ran with the mime database folder that was just updated as the first argument.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;update-mime-database&lt;&#x2F;code&gt; is the official program for rebuilding the mime database.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;relevant-specifications&quot;&gt;Relevant Specifications&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Specifications&#x2F;shared-mime-info-spec&#x2F;&quot;&gt;XDG Shared MIME-info Database&lt;&#x2F;a&gt; which allows storing information about mime-types themselves.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Specifications&#x2F;mime-apps-spec&#x2F;&quot;&gt;XDG MIME Applications Associations&lt;&#x2F;a&gt; which are responsible for mapping applications to mime-types.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;until-next-time&quot;&gt;Until Next Time …&lt;&#x2F;h2&gt;
&lt;p&gt;On expectations: When starting to write this article I didn&#x27;t expect it getting this long and technical, but researching as well as finding and fixing bugs ended up producing a lot of information which I didn&#x27;t want to hide.&lt;&#x2F;p&gt;
&lt;p&gt;A huge thank you goes to Kevin Krammer and Jeremy White for starting the &lt;code&gt;xdg-utils&lt;&#x2F;code&gt; project, to Simon Lees who is currently the de-facto maintainer and everyone who contributed in any way. And while the &lt;code&gt;xdg-utils&lt;&#x2F;code&gt; might be a mess, they are a bloody awesome and quite functional mess!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Recently &lt;code&gt;xdg-utils&lt;&#x2F;code&gt; has received a &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;releases&#x2F;v1.2.0-beta1&quot;&gt;beta release&lt;&#x2F;a&gt; after a very long time.&lt;&#x2F;p&gt;
&lt;p&gt;You can help the project by testing it, reporting and researching issues, giving context and writing fixes or documentation.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Next up on this mini-series will probably be a dive into &lt;code&gt;xdg-settings&lt;&#x2F;code&gt;, but please don&#x27;t quote me on that.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>&quot;It just opens files&quot; - xdg-open under the hood</title>
        <published>2023-09-02T00:00:00+00:00</published>
        <updated>2023-09-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/xdg-open-under-the-hood/"/>
        <id>https://slatecave.net/blog/xdg-open-under-the-hood/</id>
        
        <summary type="text">How the logic behind the freedesktop file opener works</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/xdg-open-under-the-hood/">&lt;h2 id=&quot;what-is-xdg-open&quot;&gt;What is xdg-open ?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;portland.freedesktop.org&#x2F;doc&#x2F;xdg-open.html&quot;&gt;&lt;code&gt;xdg-open&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, part of the &lt;a href=&quot;https:&#x2F;&#x2F;freedesktop.org&#x2F;wiki&#x2F;Software&#x2F;xdg-utils&#x2F;&quot;&gt;xdg-utils&lt;&#x2F;a&gt; package is a &lt;a href=&quot;https:&#x2F;&#x2F;freedesktop.org&quot;&gt;freedesktop.org&lt;&#x2F;a&gt; tool that aims to give you (and everyone else) a desktop independent interface for opening a file or URL, taking care of all the environments quirks and settings so the application or script that &amp;quot;just wants to open a file&amp;quot; doesn&#x27;t have to.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Examples for opening a file, a URI and a &lt;code&gt;file:&#x2F;&#x2F;&lt;&#x2F;code&gt; URI with &lt;code&gt;xdg-open&lt;&#x2F;code&gt;&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;xdg-open&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; example.txt&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;xdg-open&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; https:&#x2F;&#x2F;slatecave.net&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;xdg-open&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; file:&#x2F;&#x2F;&#x2F;usr&#x2F;bin&#x2F;xdg-open&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;This article is based on version &lt;code&gt;1.1.3&lt;&#x2F;code&gt;, the version that is currently in development is called &lt;code&gt;1.1.3+&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;questions&quot;&gt;Questions&lt;&#x2F;h2&gt;
&lt;p&gt;Before starting out I was trying to find answers to the following questions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How does &lt;code&gt;xdg-open&lt;&#x2F;code&gt; know my favourite applications?&lt;&#x2F;li&gt;
&lt;li&gt;Which ways are there to configure it?&lt;&#x2F;li&gt;
&lt;li&gt;Is there an interface to extending it?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is why this article is about high level logic rather than little tricks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;first-look-at-the-file&quot;&gt;First look at the file&lt;&#x2F;h2&gt;
&lt;p&gt;To analyse any tool one should try to get it&#x27;s source code.&lt;&#x2F;p&gt;
&lt;p&gt;A quick &lt;code&gt;command -v xdg-open&lt;&#x2F;code&gt; reveals the binary to be under &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;xdg-open&lt;&#x2F;code&gt; for me, &lt;code&gt;file &#x2F;usr&#x2F;bin&#x2F;xdg-open&lt;&#x2F;code&gt; tells me that it looks like a &lt;q&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;POSIX&quot;&gt;POSIX&lt;&#x2F;a&gt; shell script&lt;&#x2F;q&gt; and a &lt;code&gt;shellcheck &#x2F;usr&#x2F;bin&#x2F;xdg-open&lt;&#x2F;code&gt; command reveals that &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;file&quot;&gt;&lt;code&gt;file&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; was not quite right about the POSIX part.&lt;&#x2F;p&gt;
&lt;p&gt;But it is a shellscript that anyone can just open in their favourite text file viewer&#x2F;editor.&lt;&#x2F;p&gt;
&lt;p&gt;The file contains the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;UI code to make it easier to use&lt;&#x2F;li&gt;
&lt;li&gt;Helpers for querying &lt;code&gt;.desktop&lt;&#x2F;code&gt; files&lt;&#x2F;li&gt;
&lt;li&gt;Lots of checks for a lot of things&lt;&#x2F;li&gt;
&lt;li&gt;cli parser code&lt;&#x2F;li&gt;
&lt;li&gt;Heuristics for detecting desktop environments&lt;&#x2F;li&gt;
&lt;li&gt;Workarounds for tools that seem to misbehave&lt;&#x2F;li&gt;
&lt;li&gt;Wrappers for various desktop specific openers&lt;&#x2F;li&gt;
&lt;li&gt;Heuristics spaghetti with lots of &amp;quot;xdg&amp;quot; and &amp;quot;generic&amp;quot; in them (I assume those are the really interesting parts)&lt;&#x2F;li&gt;
&lt;li&gt;One giant switch that decides what to do&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In case you don&#x27;t have a local copy of a recent xdg-open: Its source lives in the freedesktop gitlab in &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-open.in&quot;&gt;xdg-open.in&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-utils-common.in&quot;&gt;xdg-utils-common.in&lt;&#x2F;a&gt; which is reused across the entire &lt;code&gt;xdg-urils&lt;&#x2F;code&gt; package. This article will link to the &lt;code&gt;1.1.3&lt;&#x2F;code&gt; tag instead of the latest version.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-does-it-do&quot;&gt;What does it do?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;xdg-open&lt;&#x2F;code&gt; really only does one thing, open whatever one throws at it and therefore has a pretty linear flow.&lt;&#x2F;p&gt;
&lt;p&gt;It only has options to query help and version information, I&#x27;ll ignore those code parts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Those options are checked for near the start of the script so a &lt;code&gt;xdg-open --help&lt;&#x2F;code&gt; doesn&#x27;t even have to load all the logic.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is an undocumented &lt;code&gt;XDG_UTILS_DEBUG_LEVEL&lt;&#x2F;code&gt; environment variable that makes &lt;code&gt;xdg-open&lt;&#x2F;code&gt; more chatty when set to &lt;code&gt;1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;get one URL or filename from the cli arguments&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#detecting-the-desktop&quot;&gt;try to find out which desktop is currently in use using &lt;code&gt;detectDE&lt;&#x2F;code&gt;.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;fall back to a &lt;code&gt;generic&lt;&#x2F;code&gt; desktop if detection wasn&#x27;t successful.&lt;&#x2F;li&gt;
&lt;li&gt;remove itself from the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#desktop-specific-openers&quot;&gt;based on the detected desktop call the appropriate helper for opening the file.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;desktops&quot;&gt;Desktops&lt;&#x2F;h2&gt;
&lt;p&gt;The &amp;quot;desktops&amp;quot; &lt;code&gt;xdg-open&lt;&#x2F;code&gt; is aware of are (in order of them appearing):&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;kde&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Both modern day and very old &lt;a href=&quot;https:&#x2F;&#x2F;kde.org&quot;&gt;KDE&lt;&#x2F;a&gt; desktops.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;dde&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a href=&quot;https:&#x2F;&#x2F;www.deepin.org&#x2F;en&#x2F;dde&#x2F;&quot;&gt;Deepin Desktop Environment&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;gnome3&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a href=&quot;https:&#x2F;&#x2F;gnome.org&quot;&gt;GNOME desktop&lt;&#x2F;a&gt; in versions 3 (and 4)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;cinnamon&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;projects.linuxmint.com&#x2F;cinnamon&#x2F;&quot;&gt;Cinnamon&lt;&#x2F;a&gt;, the &lt;a href=&quot;https:&#x2F;&#x2F;linuxmint.com&#x2F;&quot;&gt;Linux Mint&lt;&#x2F;a&gt; Desktop, it is treated as if it was &lt;code&gt;gnome3&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;gnome&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The GNOME desktop in version 2
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;mate&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a href=&quot;https:&#x2F;&#x2F;mate-desktop.org&#x2F;&quot;&gt;MATE Desktop Environment&lt;&#x2F;a&gt; that continues as a modern version of GNOME 2, but gets treated differently.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;xfce&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a href=&quot;https:&#x2F;&#x2F;xfce.org&quot;&gt;Xfce Desktop Environment&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;lxde&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a href=&quot;https:&#x2F;&#x2F;lxde.org&quot;&gt;Lightweight X Desktop Environment&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;lxqt&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a href=&quot;https:&#x2F;&#x2F;lxqt-project.org&#x2F;&quot;&gt;Lightweight Qt Desktop Environment&lt;&#x2F;a&gt;, a qt remake of Lxde, but not related enough to share code here.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;enlightenment&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;a href=&quot;https:&#x2F;&#x2F;www.enlightenment.org&#x2F;&quot;&gt;Enlightenment&lt;&#x2F;a&gt; Desktop.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Some of them aren&#x27;t even desktops:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;cygwin&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		We are running under Windows in a &lt;a href=&quot;https:&#x2F;&#x2F;www.cygwin.com&#x2F;&quot;&gt;Cygwin&lt;&#x2F;a&gt; environment.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;wsl&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		We are running in a WSL environment. Note that this is not yet present in version &lt;code&gt;1.1.3&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;darwin&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		We are running on MacOS.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;flatpak&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		We are in a &lt;a href=&quot;https:&#x2F;&#x2F;www.flatpak.org&#x2F;&quot;&gt;flatpak&lt;&#x2F;a&gt; container.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;generic&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This is a desktop that doesn&#x27;t require any special treatment 🥳
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Good news: many of those seem to map to the &lt;code&gt;open_generic&lt;&#x2F;code&gt; function, but for some desktops assumptions are made.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;detecting-the-desktop&quot;&gt;Detecting the desktop&lt;&#x2F;h3&gt;
&lt;p&gt;Desktop detection is done by the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-utils-common.in#L270&quot;&gt;&lt;code&gt;detectDE&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; using multiple heuristics in order of decreasing accuracy. The result is written to the &lt;code&gt;DE&lt;&#x2F;code&gt; global script variable.&lt;&#x2F;p&gt;
&lt;p&gt;The following tests are executed in order given that all previous tests didn&#x27;t match:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Test if the &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt; environment variable contains a known value.&lt;&#x2F;li&gt;
&lt;li&gt;Test for desktop specific clues
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;KDE_FULL_SESSION&lt;&#x2F;code&gt; is set&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;GNOME_DESKTOP_SESSION_ID&lt;&#x2F;code&gt; is set&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;MATE_DESKTOP_SESSION_ID&lt;&#x2F;code&gt; is set&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;org.gnome.SessionManager&lt;&#x2F;code&gt; name is owned by some application on the d-bus&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;_DT_SAVE_MODE&lt;&#x2F;code&gt; xprop on the root window contains the string &lt;code&gt;xfce4&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;xprop -root&lt;&#x2F;code&gt; has &lt;code&gt;xfce_desktop_window&lt;&#x2F;code&gt; at the start of a line in its output&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;DESKTOP&lt;&#x2F;code&gt; environment variable begins with &lt;code&gt;Enlightenment&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;LXQT_SESSION_CONFIG&lt;&#x2F;code&gt; is set&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Test if the &lt;code&gt;DESKTOP_SESSION&lt;&#x2F;code&gt; environment variable contains a known value.&lt;&#x2F;li&gt;
&lt;li&gt;Test if &lt;code&gt;uname&lt;&#x2F;code&gt; outputs either &lt;code&gt;CYGWIN&lt;&#x2F;code&gt;, &lt;code&gt;Darwin&lt;&#x2F;code&gt; or &lt;code&gt;Linux&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;In case of Linux, if &lt;code&gt;&#x2F;proc&#x2F;version&lt;&#x2F;code&gt; contains the string &lt;code&gt;microsoft&lt;&#x2F;code&gt; and the command &lt;code&gt;powershell.exe&lt;&#x2F;code&gt; is present, assume &lt;code&gt;wsl&lt;&#x2F;code&gt;. (This &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;commit&#x2F;d9e547b64b738ecb53dfb3497842500b8f6368b9&quot;&gt;was added very recently&lt;&#x2F;a&gt; and is not in version &lt;code&gt;1.1.3&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The following tests always run:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;When the desktop is a &lt;code&gt;gnome&lt;&#x2F;code&gt;, test if the &lt;code&gt;gnome-default-applications-properties&lt;&#x2F;code&gt; command is present, if not, assume &lt;code&gt;gnome3&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Test if &lt;code&gt;$XDG_RUNTIME_DIR&#x2F;flatpak-info&lt;&#x2F;code&gt; is present and if yes assume &lt;code&gt;flatpak&lt;&#x2F;code&gt; (Why isn&#x27;t this the first check?)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Because this is implemented in &lt;code&gt;xdg-utils-common.in&lt;&#x2F;code&gt; this also applies to all of the other xdg-utils tools.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;desktop-specific-openers&quot;&gt;Desktop specific openers&lt;&#x2F;h3&gt;
&lt;p&gt;Since &lt;code&gt;xdg-open&lt;&#x2F;code&gt; tries to offload the work when the desktop environment has its own mechanism for opening files lets cover those first.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;desktops-that-just-call-an-opener&quot;&gt;Desktops that &amp;quot;just call an opener&amp;quot;&lt;&#x2F;h4&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Desktop&lt;&#x2F;th&gt;&lt;th&gt;Opener&lt;&#x2F;th&gt;&lt;th&gt;Fall back to &lt;code&gt;open_generic&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;dde&lt;&#x2F;td&gt;&lt;td&gt;dde-open&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;enlightenment&lt;&#x2F;td&gt;&lt;td&gt;enlightenment&lt;wbr&gt;_open&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;lxqt&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;always&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;cygwin&lt;&#x2F;td&gt;&lt;td&gt;cygstart&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;darwin&lt;&#x2F;td&gt;&lt;td&gt;open&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;wsl&lt;&#x2F;td&gt;&lt;td&gt;powershell.exe start&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h4 id=&quot;flatpak&quot;&gt;flatpak&lt;&#x2F;h4&gt;
&lt;p&gt;When in a flatpak &lt;code&gt;xdg-open&lt;&#x2F;code&gt; invokes the &lt;code&gt;Desktop&lt;&#x2F;code&gt; portals &lt;code&gt;OpenURI&lt;&#x2F;code&gt; function using &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;gdbus&quot;&gt;&lt;code&gt;gdbus&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;gdbus&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; call&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;session&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;    --&lt;&#x2F;span&gt;dest&lt;&#x2F;span&gt; org.freedesktop.portal.Desktop &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;    --&lt;&#x2F;span&gt;object-path&lt;&#x2F;span&gt; &#x2F;org&#x2F;freedesktop&#x2F;portal&#x2F;desktop &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;    --&lt;&#x2F;span&gt;method&lt;&#x2F;span&gt; org.freedesktop.portal.OpenURI.OpenURI &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;    &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-group z-expansion z-brace z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-brace z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-brace z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; for the &lt;code&gt;1.1.3+&lt;&#x2F;code&gt; version &lt;code&gt;xdg-open&lt;&#x2F;code&gt; uses the &lt;code&gt;org.freedesktop.portal.OpenURI.OpenFile&lt;&#x2F;code&gt; method for files and file URIs. It also added a timeout of 5 seconds.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gnome-mate-and-xfce&quot;&gt;GNOME, MATE and Xfce&lt;&#x2F;h4&gt;
&lt;p&gt;GNOME 2, GNOME 3, MATE and Xfce are related in that they all share a similar cascade of openers (falling back to the next one, if the previous one isn&#x27;t present) is used.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;exo-open&lt;&#x2F;code&gt; when on xfce&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gio open&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gvfs-open&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gnome-open&lt;&#x2F;code&gt; when on &lt;code&gt;gnome&lt;&#x2F;code&gt; (GNOME 2)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;mate-open&lt;&#x2F;code&gt; when on &lt;code&gt;mate&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;open_generic&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; for GNOME 3 the only options really are &lt;code&gt;gio open&lt;&#x2F;code&gt; and &lt;code&gt;gvfs-open&lt;&#x2F;code&gt; before falling back to the generic opener.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;kde&quot;&gt;KDE&lt;&#x2F;h4&gt;
&lt;p&gt;For KDE, if the &lt;code&gt;KDE_SESSION_VERSION&lt;&#x2F;code&gt; environment variable is set &lt;code&gt;kde-open&lt;&#x2F;code&gt; is used for KDE 4 and &lt;code&gt;kde-open5&lt;&#x2F;code&gt; for for KDE 5. Otherwise &lt;code&gt;kfmclient exec&lt;&#x2F;code&gt; (with some logic to fix the exit code) is used for even older KDE versions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;commit&#x2F;c10cdaf8a03997cc18e51ee6299f0dcc02c34870&quot;&gt;KDE 6 will use &lt;code&gt;kde-open&lt;&#x2F;code&gt; again&lt;&#x2F;a&gt;, but that isn&#x27;t in version &lt;code&gt;1.1.3&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;lxde&quot;&gt;Lxde&lt;&#x2F;h4&gt;
&lt;p&gt;For Lxde &lt;code&gt;pcmanfm&lt;&#x2F;code&gt; is used if present, but only for filepaths and &lt;code&gt;file:&#x2F;&#x2F;&lt;&#x2F;code&gt; URIs. Otherwise falling back to &lt;code&gt;open_generic&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;generic-opener&quot;&gt;Generic Opener&lt;&#x2F;h2&gt;
&lt;p&gt;The generic opener is invoked by the calling the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-open.in#L402&quot;&gt;&lt;code&gt;open_generic&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; with the filepath or URI as the only argument.&lt;&#x2F;p&gt;
&lt;p&gt;This is yet another heuristics spaghetti function that tries to delegate work to other tools that may already be configured.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;opening-files&quot;&gt;Opening files&lt;&#x2F;h3&gt;
&lt;p&gt;In case the passed argument looks like a filepath or &lt;code&gt;file:&#x2F;&#x2F;&lt;&#x2F;code&gt; URI, &lt;code&gt;xdg-open&lt;&#x2F;code&gt; tries some additional openers.&lt;&#x2F;p&gt;
&lt;p&gt;If the file doesn&#x27;t exist the script will exit with an appropriate error message.&lt;&#x2F;p&gt;
&lt;p&gt;If an X or Wayland display is present (checked using &lt;code&gt;has_display&lt;&#x2F;code&gt;) the files mime-type is determined using &lt;code&gt;xdg-mime query filetype&lt;&#x2F;code&gt; and the function &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#open-generic-xdg-mime&quot;&gt;&lt;code&gt;open_generic_xdg_mime&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is attempted for opening.&lt;&#x2F;p&gt;
&lt;p&gt;If no display is present or &lt;code&gt;open_generic_xdg_mime&lt;&#x2F;code&gt; returned, opening with &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;run-mailcap&quot;&gt;&lt;code&gt;run-mailcap&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is attempted if present. &lt;code&gt;run-mailcap&lt;&#x2F;code&gt; apparently is the Debian solution to the problem from before freedesktop standardised file opening.&lt;&#x2F;p&gt;
&lt;p&gt;If we have a display and &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;mimeopen&quot;&gt;&lt;code&gt;mimeopen&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; from the &lt;a href=&quot;https:&#x2F;&#x2F;metacpan.org&#x2F;dist&#x2F;File-MimeInfo&quot;&gt;File-MimeInfo perl package&lt;&#x2F;a&gt; is present attempt to open using that.&lt;&#x2F;p&gt;
&lt;p&gt;If all of those miss then the &lt;code&gt;open_generic&lt;&#x2F;code&gt; function continues with treating the first argument given to it as if it was an URI.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;opening-uris&quot;&gt;Opening URIs&lt;&#x2F;h3&gt;
&lt;p&gt;For URIs the following happens until one of the steps finds a way to open the URI.&lt;&#x2F;p&gt;
&lt;p&gt;If there is a display (X or Wayland, checked by &lt;code&gt;has_display&lt;&#x2F;code&gt;) try to derive a URI scheme and open it using &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#open-generic-xdg-mime&quot;&gt;&lt;code&gt;open_generic_xdg_mime&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; with a mime-type of &lt;code&gt;x-scheme-handler&#x2F;&amp;lt;scheme&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This allows registering custom URI handlers using the already existing mechanism for mime-types.&lt;&#x2F;p&gt;
&lt;p&gt;If the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable (a colon &lt;code&gt;:&lt;&#x2F;code&gt; separated command list) is set try all browser commands in that variable (if &lt;code&gt;%s&lt;&#x2F;code&gt; is present insert the URI in the desired position using &lt;code&gt;printf&lt;&#x2F;code&gt;) until one of them succeeds. (implemented in &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-open.in#L374&quot;&gt;&lt;code&gt;open_envvar&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;If unset try a set of sane defaults depending on whether a display is present or not.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;code&gt;BROWSER&lt;&#x2F;code&gt; variable was cleaned up at the start of &lt;code&gt;xdg-open&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At the time of writing the list of browser commands was …&lt;&#x2F;p&gt;
&lt;p&gt;… when a display is present:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;x-www-browser&lt;&#x2F;li&gt;
&lt;li&gt;firefox&lt;&#x2F;li&gt;
&lt;li&gt;iceweasel&lt;&#x2F;li&gt;
&lt;li&gt;seamonkey&lt;&#x2F;li&gt;
&lt;li&gt;mozilla&lt;&#x2F;li&gt;
&lt;li&gt;epiphany&lt;&#x2F;li&gt;
&lt;li&gt;konqueror&lt;&#x2F;li&gt;
&lt;li&gt;chromium&lt;&#x2F;li&gt;
&lt;li&gt;chromium-browser&lt;&#x2F;li&gt;
&lt;li&gt;google-chrome&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;… for the terminal (always attempted):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;www-browser&lt;&#x2F;li&gt;
&lt;li&gt;links2&lt;&#x2F;li&gt;
&lt;li&gt;elinks&lt;&#x2F;li&gt;
&lt;li&gt;links&lt;&#x2F;li&gt;
&lt;li&gt;lynx&lt;&#x2F;li&gt;
&lt;li&gt;w3m&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;open-generic-xdg-mime&quot;&gt;&lt;code&gt;open_generic_xdg_mime&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;xdg&#x2F;xdg-utils&#x2F;-&#x2F;blob&#x2F;v1.1.3&#x2F;scripts&#x2F;xdg-open.in#L335&quot;&gt;This function&lt;&#x2F;a&gt; takes a filepath (or URI) and a mime-type as input.&lt;&#x2F;p&gt;
&lt;p&gt;It uses &lt;code&gt;xdg-mime query default&lt;&#x2F;code&gt; to get the name of the &lt;code&gt;.desktop&lt;&#x2F;code&gt; file, then searches for that in all &lt;code&gt;$XDG_DATA_DIRS&#x2F;applications&#x2F;&lt;&#x2F;code&gt;, when found parses the &lt;code&gt;Exec&lt;&#x2F;code&gt; and other needed fields, runs the resulting command and makes the script exit.&lt;&#x2F;p&gt;
&lt;p&gt;If it didn&#x27;t find a matching file it returns.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;xdg-open&lt;&#x2F;code&gt; tries to open a file or URI.&lt;&#x2F;p&gt;
&lt;p&gt;If it recognises the environment and knows a &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;xdg-open-under-the-hood&#x2F;#desktop-specific-openers&quot;&gt;opener that is specific to that environment&lt;&#x2F;a&gt; it will use that.&lt;&#x2F;p&gt;
&lt;p&gt;If there is no specific opener it will fall back to a cascade of generic openers, preferring the freedesktop way, falling back to legacy openers. The freedesktop mime-type to application mapping uses &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;xdg-mime&quot;&gt;&lt;code&gt;xdg-mime&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For URIs it will attempt to open the URI as if it was a file with the mime-type &lt;code&gt;x-scheme-handler&#x2F;&amp;lt;scheme&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The last configurable fallback is the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable which is a colon &lt;code&gt;:&lt;&#x2F;code&gt; separated list of commands of which all will be tried until the first one succeeds. If &lt;code&gt;%s&lt;&#x2F;code&gt; is present in the command the URI or filepath will be inserted in its place instead of being appended.&lt;&#x2F;p&gt;
&lt;p&gt;If the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable is empty, it falls back to a list of hard coded default applications.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;extending&quot;&gt;Extending&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;For fully custom behaviour wrapping &lt;code&gt;xdg-open&lt;&#x2F;code&gt; still seems to be the best choice.&lt;&#x2F;li&gt;
&lt;li&gt;Defaults can be customised by setting a handler in the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; environment variable.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;xdg-open&lt;&#x2F;code&gt; listens to what &lt;code&gt;xdg-mime&lt;&#x2F;code&gt; says on which mime-type a file is and which application to use for opening.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;expectations&quot;&gt;Expectations&lt;&#x2F;h2&gt;
&lt;p&gt;That was quite a bit more desktop-specific stuff in there than expected, while the generic part was supisingly simple at the high level logic view.&lt;&#x2F;p&gt;
&lt;p&gt;One thing that was unexpected was that there is no obvious connection to &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;xdg-settings&quot;&gt;&lt;code&gt;xdg-settings&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; i.e. for the default web browser, but maybe that is hidden in &lt;code&gt;xdg-mime&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll try to follow up with articles on both tools. Make sure you subscribe to my &lt;a href=&quot;&#x2F;atom.xml&quot;&gt;atom feed&lt;&#x2F;a&gt; 😉. Bye!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Improving an fzf example with null bytes</title>
        <published>2023-07-09T00:00:00+00:00</published>
        <updated>2023-07-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/improving-an-fzf-example/"/>
        <id>https://slatecave.net/blog/improving-an-fzf-example/</id>
        
        <summary type="text">or: Why good shell scripts are sometimes bad for explaining</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/improving-an-fzf-example/">&lt;h2 id=&quot;what-is-the-problem&quot;&gt;What is the problem?&lt;&#x2F;h2&gt;
&lt;p&gt;On the &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fzf#preview-window=&quot;&gt;fzf manpage&lt;&#x2F;a&gt; the &lt;code&gt;--preview-window&lt;&#x2F;code&gt; option is shown off using a combination of &lt;code&gt;git grep&lt;&#x2F;code&gt; for querying every line of every file in the current git repository, &lt;code&gt;fzf&lt;&#x2F;code&gt; for searching and &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;bat&quot;&gt;&lt;code&gt;bat&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for a preview with the line currently selected in fzf highlighted and scrolled into the middle of the preview window. (pretty awesome example if you asked me.)&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The example from the manpage&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Preview with bat, matching line in the middle of the window below&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; the fixed header of the top 3 lines&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; […]&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; grep&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;line-number&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;fzf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;delimiter&lt;&#x2F;span&gt; : &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;	--&lt;&#x2F;span&gt;preview&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;bat --style=full --color=always --highlight-line {2} {1}&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;	--&lt;&#x2F;span&gt;preview-window&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;~3,+{2}+3&#x2F;2&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;The example works by relying on the fact that the output of the &lt;code&gt;git grep&lt;&#x2F;code&gt; looks like &lt;code&gt;&amp;lt;filename&amp;gt;:&amp;lt;linenumber: …&lt;&#x2F;code&gt;, split at the colon, &lt;code&gt;{1}&lt;&#x2F;code&gt; is the filename, &lt;code&gt;{2}&lt;&#x2F;code&gt; is the line number.&lt;&#x2F;p&gt;
&lt;p&gt;While the example is very good at explaining how the &lt;code&gt;--preview-window&lt;&#x2F;code&gt; interacts with the &lt;code&gt;--delimiter&lt;&#x2F;code&gt; (its primary purpose), it breaks once there is a colon somewhere in a filename, then &lt;code&gt;{1}&lt;&#x2F;code&gt; and &lt;code&gt;{2}&lt;&#x2F;code&gt; become two halves of an incomplete filename and bat has no idea what it should do with that.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example output produced by &lt;code&gt;git grep --line-number &quot;&quot;&lt;&#x2F;code&gt;. Note that the last line looks a bit funny.&lt;&#x2F;figcaption&gt;
	&lt;pre&gt;&lt;samp&gt;test.txt:1:Lorem ipsum
test.txt:2:dolor sid …
throw:off:1:Does this filename confuse fzf?
&lt;&#x2F;samp&gt;&lt;&#x2F;pre&gt;
	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;let-s-fix-it&quot;&gt;Let&#x27;s fix it!&lt;&#x2F;h2&gt;
&lt;p&gt;The Solution is actually pretty simple: &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;git-grep&quot;&gt;&lt;code&gt;git grep&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; supports a &lt;code&gt;-z&lt;&#x2F;code&gt; flag, making it use null bytes instead of colons for separating filename, line number and content, tell fzf to use that as the delimiter using &lt;code&gt;--delimiter &#x27;\0&#x27;&lt;&#x2F;code&gt; and, the preview isn&#x27;t confused anymore.&lt;&#x2F;p&gt;
&lt;p&gt;One remaining problem is that the menu looks like this: &lt;samp&gt;test.txt1Lorem ipsum&lt;&#x2F;samp&gt;, not very readable because those nullbytes are invisible.&lt;&#x2F;p&gt;
&lt;p&gt;But there is nothing stopping us from replacing all null bytes with nullbytes followed by a colon and telling fzf to use that combination as a delimiter. &lt;code&gt;git&lt;&#x2F;code&gt; doesn&#x27;t have something built in for that, but that is what &lt;code&gt;sed&lt;&#x2F;code&gt; was built for.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The example from the manpage modified to use null separators in addition to human readable ones.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; grep&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;z&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;line-number&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;E&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;s|\x00|\x00:|g&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;fzf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;delimiter&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;\0:&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;    --&lt;&#x2F;span&gt;preview&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;bat --style=full --color=always --highlight-line {2} {1}&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;    --&lt;&#x2F;span&gt;preview-window&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;~3,+{2}+3&#x2F;2&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;In case this looks a bit dull add a &lt;code&gt;--color=auto&lt;&#x2F;code&gt; to the git command and a &lt;code&gt;--ansi&lt;&#x2F;code&gt; to fzf.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;done&quot;&gt;Done&lt;&#x2F;h2&gt;
&lt;p&gt;Given that the more correct version takes noticeably more brain-cycles to decode it is no surprise they used the simple version for the manual.&lt;&#x2F;p&gt;
&lt;p&gt;In case you want to: &lt;a href=&quot;&#x2F;notebook&#x2F;shell-null-termination&quot;&gt;Learn more about null separation in the shell&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Happy experimenting!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Environment Variable Zoo</title>
        <published>2023-07-03T00:00:00+00:00</published>
        <updated>2023-11-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/env/"/>
        <id>https://slatecave.net/notebook/env/</id>
        
        <summary type="text">Environment Variables for Linux you might want to know about.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/env/">&lt;p&gt;Environment variables are basically inherited, named arguments to processes with a huge impact on how a Linux or other *nix systems work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reading-and-writing-environment-variables&quot;&gt;Reading and Writing Environment Variables&lt;&#x2F;h2&gt;
&lt;p&gt;These commands here should work in almost all shells inspired by the original sh, if you use something else you probably know why and how to use it.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		Read out a single Variable
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;echo &amp;quot;$SHELL&amp;quot;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;printenv SHELL&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		Dump out all variables
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;export&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;printenv&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		Set one or more variable for a single command
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;TZ=UTC LANG=&amp;quot;de_DE.UTF-8&amp;quot; date&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;env TZ=UTC LANG=&amp;quot;de_DE.UTF-8&amp;quot; date&lt;&#x2F;code&gt; Useful program for simpler shells, environment (un)setting and &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Chain_loading#Chain_loading_in_Unix&quot;&gt;chain-loading&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		Set a variable for the currently running shell
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;export TZ=UTC&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;declare -x TZ=UTC&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;persisting-environment-variables&quot;&gt;Persisting Environment Variables&lt;&#x2F;h3&gt;
&lt;p&gt;For permanently setting some environment variables there are a number of options (Options further up usually can override the ones below them).&lt;&#x2F;p&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;.bashrc&lt;&#x2F;code&gt;, &lt;code&gt;.zshrc&lt;&#x2F;code&gt;, etc.
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Read whenever you start your favourite shell (as a non-login shell), but don&#x27;t assume it has effects on the whole session, it is not made for that.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		Your Desktop Environment
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Desktops like setting environment variables derived from their settings, sometimes for single programs, sometimes for the whole session.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;.profile&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Read on every login (your login manager is responsible for doing this) and applies to your whole session.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;&#x2F;etc&#x2F;profile.d&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;etc&#x2F;profile&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		System-wide &lt;code&gt;.profile&lt;&#x2F;code&gt; equivalent, remember that you are sharing this one with your packages.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		PAM
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pam&quot;&gt;PAM&lt;&#x2F;a&gt; also sets environment variables.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The &lt;code&gt;.bashrc&lt;&#x2F;code&gt; (plus friends) and &lt;code&gt;.profile&lt;&#x2F;code&gt; can be reloaded by running &lt;code&gt;source ~&#x2F;&amp;lt;filename&amp;gt;&lt;&#x2F;code&gt; or &lt;code&gt;. ~&#x2F;.filename&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Non POSIX shells usually have their own &lt;code&gt;.profile&lt;&#x2F;code&gt; equivalent, keep that in mind when changing your login shell. Bash for example uses &lt;code&gt;.bash_profile&lt;&#x2F;code&gt; if it exists.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;spying-on-other-processes&quot;&gt;Spying on other Processes&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes it is useful to know what environment variables a given running process has currently set, if the process belongs to you or you have root rights (use them responsibly) you have access to the file that contains the process environment.&lt;&#x2F;p&gt;
&lt;p&gt;The files path is &lt;code&gt;&#x2F;proc&#x2F;$pid&#x2F;environ&lt;&#x2F;code&gt; (Substitute &lt;code&gt;$pid&lt;&#x2F;code&gt; for the process id), it contains the null separated environment variables.&lt;&#x2F;p&gt;
&lt;p&gt;To get them in a pretty way one can use the command &lt;code&gt;tr &#x27;\0&#x27; &#x27;\n&#x27; &amp;lt; &#x2F;proc&#x2F;$pid&#x2F;environ&lt;&#x2F;code&gt; (pipe it into the tr command which replaces the null separators with newlines).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;htop&lt;&#x2F;code&gt; shows the selected processes environment when pressing &lt;kbd&gt;e&lt;&#x2F;kbd&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; This is the reason why it usually is a bad idea to use environment variables for secrets, they can be read out by any other process running under the same user. When transporting secrets from one program to another use pipes and features of your shell to directly read from and write to them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;finding-documentation&quot;&gt;Finding Documentation&lt;&#x2F;h2&gt;
&lt;p&gt;Outside of the usual online sources: Read the documentation and read the manpages. Most manpages have an &lt;code&gt;ENVIRONMENT&lt;&#x2F;code&gt; section describing which variables the program reads and&#x2F;or sets.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;man.voidlinux.org&amp;#x2F;environ&quot;&gt;
		environ(7) - The manpage about the basics of environment variables.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;linux-and-posix&quot;&gt;Linux and Posix&lt;&#x2F;h2&gt;
&lt;p&gt;You can find a more complete list of the standardised variables in the &lt;a href=&quot;https:&#x2F;&#x2F;pubs.opengroup.org&#x2F;onlinepubs&#x2F;9699919799&#x2F;basedefs&#x2F;V1_chap08.html&quot;&gt;POSIX (2017, the latest at the time of writing) specification&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;HOME&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Contains the path to your home directory, as simple as that. (Don&#x27;t abuse for constructs like &lt;code&gt;$HOME&#x2F;.config&#x2F;whatever&lt;&#x2F;code&gt;, that&#x27;s what the XDG home variables are for.)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;PATH&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Contains a colon separated list of paths to directories that contain binary files for running them as commands. Directories closer to the front can override others counting binaries of the same name further back.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;USER&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Should contain the currently logged in username. (Fun fact: the kernel doesn&#x27;t know your username)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;SHELL&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Contains the path to the shell binary configured for the current user, this is probably how your favourite terminal emulator knows about your favourite shell without configuration. This may or may not be the shell you are currently using.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;TMPDIR&lt;&#x2F;code&gt; or &lt;code&gt;TMP&lt;&#x2F;code&gt; or &lt;code&gt;TEMP&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Almost always one of them contains a path to the preferred temporary directory. Useful for when every user gets their own directory with proper permissions. &lt;code&gt;TMPDIR&lt;&#x2F;code&gt; is the one in the POSIX standard and should be the preferred one.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;LC_*&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These contain locale information and are the reason why language settings work across desktop environments. Unfortunately also the reason why you have to restart applications after changing system preferences. For possible values see &lt;code&gt;&#x2F;etc&#x2F;locale.gen&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;etc&#x2F;locale.conf&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;LANG&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Language setting, not prefixed by &lt;code&gt;LC_&lt;&#x2F;code&gt; for reasons. Set to &lt;code&gt;C&lt;&#x2F;code&gt; for &amp;quot;no translation at all&amp;quot;, usually that means American English. For possible values see &lt;code&gt;&#x2F;etc&#x2F;locale.gen&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;etc&#x2F;locale.conf&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;TZ&lt;&#x2F;code&gt; and &lt;code&gt;TZDIR&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These describe the &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tzset#TZ&quot;&gt;current timezone to use&lt;&#x2F;a&gt; i.e. &lt;code&gt;Europe&#x2F;Berlin&lt;&#x2F;code&gt;. Usually points to a &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tzfile&quot;&gt;tzfile&lt;&#x2F;a&gt; in &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;zoneinfo&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;PAGER&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		A program that can be run using &lt;code&gt;sh -c&lt;&#x2F;code&gt; that shows a textfile (unually in the current terminal)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;EDITOR&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Like &lt;code&gt;PAGER&lt;&#x2F;code&gt;, but for your favourite text editor.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;BROWSER&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Like &lt;code&gt;BROWSER&lt;&#x2F;code&gt;, but for your favourite browser. (Not standardised anywhere, but a well known convention)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;MAILER&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Like &lt;code&gt;PAGER&lt;&#x2F;code&gt; but for opening &lt;code&gt;mailto:&lt;&#x2F;code&gt; links with your favourite E-Mail writer. (Also never standardised)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;TERM&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Contains the name of the terminal, together with the &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;terminfo&quot;&gt;terminfo&lt;&#x2F;a&gt; database the capabilities of the terminal can be found out. When remoting into other machines with a less well known terminal &lt;code&gt;xterm&lt;&#x2F;code&gt; is usually a sane fallback.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;MOTD_SHOWN&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When set it indicates who has already shown the motd to prevent showing it twice. Convention, probably best known implementation is the &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;man8&#x2F;pam_motd.8&quot;&gt;pam motd module&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; What is meant by run with &lt;code&gt;sh -c&lt;&#x2F;code&gt; is demonstrated pretty well by &lt;a href=&quot;https:&#x2F;&#x2F;salsa.debian.org&#x2F;debian&#x2F;sensible-utils&#x2F;-&#x2F;blob&#x2F;b555a8db938946c519db3d6d2a25cbe2a39945d2&#x2F;sensible-editor.in#L24&quot;&gt;debians sensible-utils&lt;&#x2F;a&gt;. In a nutshell you lauch your favorite &lt;code&gt;EDITOR&lt;&#x2F;code&gt; using &lt;code&gt;sh -c &amp;quot;$EDITOR \&amp;quot;\$@\&amp;quot;&amp;quot; EDITOR &amp;lt;filepath-goes-here&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I&#x27;m trying to move the &lt;code&gt;BROWSER&lt;&#x2F;code&gt; and &lt;code&gt;MAILER&lt;&#x2F;code&gt; variables to the XDG section. &lt;a href=&quot;https:&#x2F;&#x2F;lists.freedesktop.org&#x2F;archives&#x2F;xdg&#x2F;2023-October&#x2F;014662.html&quot;&gt;See the related discussion on the xdg mailing list.&lt;&#x2F;a&gt; You&#x27;ll have to dig around a bit to find all mails 😞.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xdg-freedesktop-org&quot;&gt;XDG freedesktop.org&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;&quot;&gt;freedesktop.org&lt;&#x2F;a&gt; hosts quite a few specifications ensuring interoperability of free (software) desktop envoirnments, these specifications happen to contain quite a lot of envoirnment variables.&lt;&#x2F;p&gt;
&lt;p&gt;XDG stands for Cross Desktop Group.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;base-directories&quot;&gt;Base Directories&lt;&#x2F;h3&gt;
&lt;p&gt;These are defines in the &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Specifications&#x2F;basedir-spec&#x2F;&quot;&gt;Desktop Base Directories Specification&lt;&#x2F;a&gt;, while the below is an overview and you should always read the specification.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; while one can usually guess where those directories are, it is &lt;em&gt;always&lt;&#x2F;em&gt; a good idea to respect the environment variables instead of blindly writing to some guessed default path, People trying to organise their Home folder will thank you.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;XDG_DATA_HOME&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Path where applications can dump their non-configuration and non-cache persistence.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt;$HOME&#x2F;.local&#x2F;share&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_CONFIG_HOME&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Path for storing configuration files (and &lt;em&gt;nothing&lt;&#x2F;em&gt; else!)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt;$HOME&#x2F;.config&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_DATA_DIRS&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		colon &lt;code&gt;:&lt;&#x2F;code&gt; separated list of fallback directories for &lt;code&gt;XDG_DATA_HOME&lt;&#x2F;code&gt; containing default data files. (similar to how &lt;code&gt;$PATH&lt;&#x2F;code&gt; works)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt; &#x2F;usr&#x2F;local&#x2F;share&#x2F;:&#x2F;usr&#x2F;share&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_CONFIG_DIRS&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		colon &lt;code&gt;:&lt;&#x2F;code&gt; separated list of fallback directories for &lt;code&gt;XDG_CONFIG_HOME&lt;&#x2F;code&gt; containing default configuration files. (similar to how &lt;code&gt;$PATH&lt;&#x2F;code&gt; works). This behaviour is very useful when administrating large desktop systems.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt;&#x2F;etc&#x2F;xdg&#x2F;&lt;&#x2F;code&gt; 
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_CACHE_HOME&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Path for storing cache files, may contain files specific to the current architecture details, may need cleaning up when on a shared filesystem.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Also be used for files that better get lost rather than ending up in a backup by accident, &lt;code&gt;keepassxc&lt;&#x2F;code&gt; is an example for that.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Default: &lt;code&gt;$HOME&#x2F;.cache&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_RUNTIME_DIR&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		For temporary runtime files like sockets, pipes, etc.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Usually: &lt;code&gt;&#x2F;run&#x2F;user&#x2F;&amp;lt;userid&amp;gt;&#x2F;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;user-directories&quot;&gt;User Directories&lt;&#x2F;h3&gt;
&lt;p&gt;There is also the possibility to define User directories, these have the form of &lt;code&gt;XDG_&amp;lt;name&amp;gt;_DIR&lt;&#x2F;code&gt; and are configured using the &lt;code&gt;$XDG_CONFIG_HOME&#x2F;user-dirs.dirs&lt;&#x2F;code&gt; and taken care of by the &lt;code&gt;xdg-user-dirs-update&lt;&#x2F;code&gt; utility&lt;&#x2F;p&gt;
&lt;p&gt;Commonly specified ones are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DESKTOP&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DOWNLOAD&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;TEMPLATES&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;PUBLICSHARE&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DOCUMENTS&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;MUSIC&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;PICTURES&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;VIDEOS&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;SCREENSHOTS&lt;&#x2F;code&gt; - Used by some popular screenshot scripts &lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;current-desktop&quot;&gt;Current Desktop&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;specifications.freedesktop.org&#x2F;desktop-entry-spec&#x2F;desktop-entry-spec-latest.html#recognized-keys&quot;&gt;Freedesktop &amp;quot;Desktop Entry Specification&amp;quot;&lt;&#x2F;a&gt; specifies a &lt;code&gt;XDG_CURRENT_DESKTOP&lt;&#x2F;code&gt; variable that is set from the &lt;code&gt;DesktopNames&lt;&#x2F;code&gt; value in desktops session file (the ones in &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;xsessions&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;wayland-sessions&#x2F;&lt;&#x2F;code&gt; and whatever &lt;code&gt;XDG_DATA_DIRS&lt;&#x2F;code&gt; points to).&lt;&#x2F;p&gt;
&lt;p&gt;It contains a colon separated list of names for the current desktop, primarily intended for activating or deactivating desktop entries (i.e. you don&#x27;t want the gnome settings cluttering up you KDE menus and vice versa).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xdg-session&quot;&gt;XDG Session&lt;&#x2F;h3&gt;
&lt;p&gt;These variables are &lt;em&gt;not&lt;&#x2F;em&gt; part of a specification, the best source for them is a &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Software&#x2F;systemd&#x2F;writing-display-managers&#x2F;&quot;&gt;guide for &amp;quot;Writing Display Managers&amp;quot; with systemd&lt;&#x2F;a&gt; and the manpage for the &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;systemd&#x2F;man&#x2F;pam_systemd.html&quot;&gt;&lt;code&gt;pam_systemd&lt;&#x2F;code&gt; module&lt;&#x2F;a&gt;. Some of them happen to be useful.&lt;&#x2F;p&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;XDG_SEAT&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		By definition set to &lt;code&gt;seat0&lt;&#x2F;code&gt;, probably reserved for future use in multiseat scenarios.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_SESSION_TYPE&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The best documentation for this one is probably the &lt;code&gt;type&lt;&#x2F;code&gt; argument for the pam_systemd module, roughly it is the display server used for the session. (&lt;code&gt;x11&lt;&#x2F;code&gt;,&lt;code&gt;wayland&lt;&#x2F;code&gt;,&lt;code&gt;mir&lt;&#x2F;code&gt;,&lt;code&gt;tty&lt;&#x2F;code&gt;,&lt;code&gt;unknown&lt;&#x2F;code&gt;)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_SESSION_CLASS&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		either &lt;code&gt;user&lt;&#x2F;code&gt; or &lt;code&gt;greeter&lt;&#x2F;code&gt; depending on whether the session is primarily logged in or for logging in.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_VTNR&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		set to the number of the TTY the current session runs in.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;code&gt;XDG_SESSION_ID&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Either the &lt;code&gt;&#x2F;proc&#x2F;self&#x2F;sessionid&lt;&#x2F;code&gt; of the auditing session or an &amp;quot;independent counter&amp;quot;.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;graphical-environment&quot;&gt;Graphical Environment&lt;&#x2F;h2&gt;




	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;DISPLAY&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The id of your X-Display, on your laptop it is probably &lt;code&gt;:0&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;WAYLAND_DISPLAY&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The name of your wayland socket file in &lt;code&gt;$XDG_RUNTIME_DIR&#x2F;&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;XCURSOR_SIZE&lt;&#x2F;code&gt;, &lt;code&gt;XCURSOR_THEME&lt;&#x2F;code&gt;, &lt;code&gt;XCURSOR_PATH&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		One way to configure how your cursor looks in most applications. It even works with wayland for when other options are not available. &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;Cursor_themes&quot;&gt;The Arch Wiki has more information on cursor configuration&lt;&#x2F;a&gt;.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		&lt;code&gt;DBUS_SESSION_BUS_ADDRESS&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Contains information on how to connect to the dbus session bus. Usually set by the &lt;code&gt;dbus-run-session&lt;&#x2F;code&gt; wrapper.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;java&quot;&gt;Java&lt;&#x2F;h2&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;_JAVA_AWT_WM_NONREPARENTING&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Tells java if you have a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Re-parenting_window_manager&quot;&gt;non-reparenting window manager&lt;&#x2F;a&gt;, usually set to 0 on modern Xorg, to 1 on wayland. If your window stays blank that is an indicator this one is wrong. (Yes the underscore at the start is intentional.)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Possible Values: &lt;code&gt;0&lt;&#x2F;code&gt;, &lt;code&gt;1&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;More Java environment variables will be added when I run into them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;&#x2F;h2&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		&lt;code&gt;KUBECONFIG&lt;&#x2F;code&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The path to a &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;configuration&#x2F;organize-cluster-access-kubeconfig&#x2F;&quot;&gt;yaml file containing connection information for a Kubernetes cluster&lt;&#x2F;a&gt; that tools like &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;reference&#x2F;kubectl&#x2F;&quot;&gt;&lt;code&gt;kubectl&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;helm.sh&quot;&gt;&lt;code&gt;helm&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; can use to connect to the cluster.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		k3s generates a &lt;code&gt;&#x2F;etc&#x2F;rancher&#x2F;k3s&#x2F;k3s.yaml&lt;&#x2F;code&gt; file you can point this at.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Fontconfig</title>
        <published>2023-06-20T00:00:00+00:00</published>
        <updated>2023-12-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/fontconfig/"/>
        <id>https://slatecave.net/notebook/fontconfig/</id>
        
        <summary type="text">Some knowledge that makes your life easier when working with fonts on Linux.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/fontconfig/">&lt;h2 id=&quot;what-is-fontconfig&quot;&gt;What is Fontconfig?&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Fontconfig is a library for configuring and customising font access.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It is built into almost every program that runs on Linux and has a GUI (even in into old java software!) and makes it very easy to make configure fonts and then forget about it.&lt;&#x2F;p&gt;
&lt;p&gt;You can find the &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;wiki&#x2F;Software&#x2F;fontconfig&#x2F;&quot;&gt;Fontconfig documentation over on freedesktop.org&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fontconfig-cli-tools&quot;&gt;Fontconfig CLI Tools&lt;&#x2F;h2&gt;
&lt;p&gt;You average Linux distribution ships Fontconfig along with some commands to poke it.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-cache&quot;&gt;fc-cache&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for rebuilding the Fontconfig cache (after installing a font or changing configuration).&lt;br&gt;Usually used like this: &lt;code&gt;fc-cache -f -v&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-conflist&quot;&gt;fc-conflist&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		gives you a list of configuration files found by Fontconfig.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-match&quot;&gt;fc-match&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for looking up which exact font a given name resolves to, &lt;em&gt;very&lt;&#x2F;em&gt; useful for debugging.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-query&quot;&gt;fc-query&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		to print out metadata for a font file.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-validate&quot;&gt;fc-validate&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		can give a hint abut missing glyphs in a font file for a given language.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-cat&quot;&gt;fc-cat&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		for having a look at the Fontconfig cache.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-list&quot;&gt;fc-list&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		to list all fonts Fontconfig knows about.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-pattern&quot;&gt;fc-pattern&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		helps you by parsing &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;FcPatternFormat&quot;&gt;Fontconfig patterns&lt;&#x2F;a&gt; , useful for debugging.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;fc-scan&quot;&gt;fc-scan&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		similar to &lt;code&gt;fc-query&lt;&#x2F;code&gt; but has the same output format as &lt;code&gt;fc-pattern&lt;&#x2F;code&gt;.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;font-family-names&quot;&gt;Font (Family) Names&lt;&#x2F;h2&gt;
&lt;p&gt;What one usually calls the fonts name actually is the font family name. This is because of historical reasons from back when a font was a case of lead letters for printing and one needed an extra case full of letters for the bold or italic styles.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;family&lt;&#x2F;code&gt; name is usually what is accepted whenever you have some configuration file and it wants a font name. Usually the postscript name works too. Both can be found out using &lt;code&gt;fc-query&lt;&#x2F;code&gt; on a given font file. &lt;code&gt;fc-list&lt;&#x2F;code&gt; also shows the family name by default.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;fc-query -b&lt;&#x2F;code&gt; omits language and character set information resulting in a more readable output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Fontconfig also has a &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;fontconfig&#x2F;fontconfig-user.html#AEN36&quot;&gt;specific font name&lt;&#x2F;a&gt; that includes the family name, size, style and every other possible font setting.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;system-fonts&quot;&gt;System Fonts&lt;&#x2F;h2&gt;
&lt;p&gt;System fonts using Fontconfig are just configuration aliasing generic names to your favourite font.&lt;&#x2F;p&gt;
&lt;p&gt;The most common are &lt;code&gt;monopace&lt;&#x2F;code&gt;, &lt;code&gt;sans&lt;&#x2F;code&gt; or &lt;code&gt;sans-serif&lt;&#x2F;code&gt; and &lt;code&gt;serif&lt;&#x2F;code&gt;. If your desktop environment has a font preference dialog in the system settings it is changing which font names those aliases point to.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is also a convention to alias &lt;code&gt;emoji&lt;&#x2F;code&gt; to your preferred emoji font.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;configuration-files&quot;&gt;Configuration Files&lt;&#x2F;h2&gt;
&lt;p&gt;You can find your Fontconfig configuration file in &lt;code&gt;$XDG_CONFIG_DIR&#x2F;fontconfig&lt;&#x2F;code&gt; which in most cases is &lt;code&gt;~&#x2F;.config&#x2F;fontconfig&#x2F;&lt;&#x2F;code&gt;. In there you can either directly edit the &lt;code&gt;fonts.conf&lt;&#x2F;code&gt; file or a file in the &lt;code&gt;conf.d&lt;&#x2F;code&gt; folder.&lt;&#x2F;p&gt;
&lt;p&gt;Global Configuration is in &lt;code&gt;&#x2F;etc&#x2F;fonts&#x2F;&lt;&#x2F;code&gt;. It usually consists of files linked from &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;fontconfig&#x2F;&lt;&#x2F;code&gt; where configuration snippets for common settings are installed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-predefined-configuration&quot;&gt;Using Predefined Configuration&lt;&#x2F;h3&gt;
&lt;p&gt;fc-conflist will list all configuration files for fontconfig, the active ones prefixed with &lt;code&gt;+&lt;&#x2F;code&gt; the inactive ones in &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;fontconfig&#x2F;&lt;&#x2F;code&gt; prefixed with a &lt;code&gt;-&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To use the predefined configuration symlink it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;ln&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;s&lt;&#x2F;span&gt; &#x2F;usr&#x2F;share&#x2F;fontconfig&#x2F;conf.avail&#x2F;70-no-bitmaps.conf &#x2F;etc&#x2F;fonts&#x2F;conf.d&#x2F;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Regenerate the fonconfig cache&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;fc-cache&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;fv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The &lt;a href=&quot;https:&#x2F;&#x2F;docs.voidlinux.org&#x2F;config&#x2F;graphical-session&#x2F;fonts.html&quot;&gt;Void Linux Documentation on Fonts&lt;&#x2F;a&gt; gives a similar example on linking the &lt;code&gt;70-no-bitmaps.conf&lt;&#x2F;code&gt; to &lt;code&gt;&#x2F;etc&#x2F;fonts&#x2F;conf.d&#x2F;&lt;&#x2F;code&gt; to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;void-linux&#x2F;void-packages&#x2F;issues&#x2F;22477&quot;&gt;mitigate the common issue that Firefox picks pixelated bitmap fonts&lt;&#x2F;a&gt; over higher quality ones.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;writing-custom-configuration&quot;&gt;Writing Custom Configuration&lt;&#x2F;h3&gt;
&lt;p&gt;To make a valid Fontconfig configuration file it needs some &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;fontconfig&#x2F;fontconfig-user.html#AEN53&quot;&gt;XML scaffolding&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-preprocessor z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-xml&quot;&gt;xml&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;version&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;1.0&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-sgml z-doctype z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-declaration z-doctype z-xml&quot;&gt;DOCTYPE&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-documentroot z-localname z-xml&quot;&gt;fontconfig&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-external-content z-xml&quot;&gt;SYSTEM&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;urn:fontconfig:fonts.dtd&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;fontconfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-comment z-block z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-xml&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt; actual configuration goes here &lt;span class=&quot;z-punctuation z-definition z-comment z-end z-xml&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;fontconfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That scaffold can now be filled with some rules.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; In case you are in need of an example, have a look at the &lt;a href=&quot;https:&#x2F;&#x2F;www.freedesktop.org&#x2F;software&#x2F;fontconfig&#x2F;fontconfig-user.html#AEN252&quot;&gt;Fontconfig documentation examples&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.config&#x2F;fontconfig&quot;&gt;my Fontconfig&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Those rule could be disabling fonts without having to uninstall them, useful when one doesn&#x27;t have root access or wants to keep the font files for some reason.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example rule for disabling FontAwesome to prevent it from interfering with other icon-fonts.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;selectfont&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;rejectfont&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
			&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;patelt&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-attribute-name z-localname z-xml&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-xml&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;family&lt;span class=&quot;z-punctuation z-definition z-string z-end z-xml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;FontAwesome&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;patelt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-comment z-block z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-xml&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt; More pattern instances for other fonts can go here &lt;span class=&quot;z-punctuation z-definition z-comment z-end z-xml&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;rejectfont&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;selectfont&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;Another application is to configure the system fonts mentioned before using &lt;code&gt;alias&lt;&#x2F;code&gt; rules. For setting the &lt;code&gt;emoji&lt;&#x2F;code&gt; (or any other) font one simply maps the family name &lt;code&gt;emoji&lt;&#x2F;code&gt; to the family name of the preferred font.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example rule for aliasing &lt;code&gt;emoji&lt;&#x2F;code&gt; to &lt;code&gt;Twemoji&lt;&#x2F;code&gt;.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;alias&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;emoji&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;prefer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Twemoji&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;prefer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;alias&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	&lt;figcaption&gt;The same mechanism could be used to alias something like &lt;code&gt;Helvetica&lt;&#x2F;code&gt; to your favourite sans font family.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The &lt;code&gt;prefer&lt;&#x2F;code&gt; part of the alias also accepts multiple font families, this can be used to apply the &lt;code&gt;emoji&lt;&#x2F;code&gt; alias to system fonts by telling Fontconfig to prefer your favourite monospace, sans or serif font &lt;b&gt;and&lt;&#x2F;b&gt; whatever is aliased to &lt;code&gt;emoji&lt;&#x2F;code&gt; over all other installed fonts.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;xml&quot; class=&quot;language-xml z-code&quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;z-text z-xml&quot;&gt;&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;alias&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;monospace&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;prefer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Noto Sans Mono&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;emoji&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;family&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;prefer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-xml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-xml&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-localname z-xml&quot;&gt;alias&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-xml&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Of course there are more applications for Fontconfig configurations, but those probably deserve more specific pages.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; I&#x27;m using Twemoji and Noto Sans, not because I like the companies that made them, but because they are under a permissive license are mostly complete, look okay and are one of the very few things those companies are actually doing good for humanity.&lt;&#x2F;p&gt;
&lt;p&gt;There are community made fonts like &lt;a href=&quot;https:&#x2F;&#x2F;rsms.me&#x2F;inter&#x2F;&quot;&gt;Inter&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;openmoji.org&#x2F;&quot;&gt;OpenMoji&lt;&#x2F;a&gt; (plus some more) out there with an impressive coverage and feature set that shouldn&#x27;t go unmentioned here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installing-fonts&quot;&gt;Installing fonts&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;from-packages&quot;&gt;From packages&lt;&#x2F;h3&gt;
&lt;p&gt;Usually the packages do everything for you and even install some sane Fontconfig rules.&lt;&#x2F;p&gt;
&lt;p&gt;If they don&#x27;t, have a look at the Fontconfig files they install and check if they clash with your own configuration or if the package description, documentation or comment in its Fontconfig file mentions additional required configuration. This is usually the case for emoji fonts to get them as the default font.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;from-files&quot;&gt;From files&lt;&#x2F;h3&gt;
&lt;p&gt;In case you have a modern Linux distribution you can install a font by dropping the ttf or otf file into &lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;fonts&#x2F;&lt;&#x2F;code&gt; or &lt;code&gt;~&#x2F;.fonts&#x2F;&lt;&#x2F;code&gt;, the latter works on older distributions too.&lt;&#x2F;p&gt;
&lt;p&gt;To apply the changes run &lt;code&gt;fc-cache -f -v&lt;&#x2F;code&gt; to rebuild the Fontconfig cache and restart your applications (or log out and back in).&lt;&#x2F;p&gt;
&lt;p&gt;Before restarting applications you could verify that the font was installed using the &lt;code&gt;fc-match&lt;&#x2F;code&gt; and &lt;code&gt;fc-list&lt;&#x2F;code&gt; commands.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;converting-woff2-to-ttf&quot;&gt;Converting woff2 to ttf&lt;&#x2F;h4&gt;
&lt;p&gt;Google (who else 🙄) made a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;woff2&quot;&gt;tool for converting woff2 fonts to ttf and back&lt;&#x2F;a&gt;, the package containing it is simply called &lt;code&gt;woff2&lt;&#x2F;code&gt; in most Linux distributions.&lt;&#x2F;p&gt;
&lt;p&gt;It contains the &lt;code&gt;woff2_decompress&lt;&#x2F;code&gt; command, give it a path to a woff2 font file and it will convert it to ttf. There is also a &lt;code&gt;woff2_compress&lt;&#x2F;code&gt; that works in the other direction.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>ANSI Escape Sequences</title>
        <published>2023-05-28T00:00:00+00:00</published>
        <updated>2023-05-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/ansi-escape-sequences/"/>
        <id>https://slatecave.net/notebook/ansi-escape-sequences/</id>
        
        <summary type="text">The things that make your Terminal colourful</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/ansi-escape-sequences/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ANSI_escape_code&quot;&gt;Ansi Escape sequences&lt;&#x2F;a&gt; are some standardised byte sequences that make most Terminals do some thing other than just printing characters on screen. Like printing colourful characters!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-them&quot;&gt;Using them&lt;&#x2F;h2&gt;
&lt;p&gt;The codes can be produced by any string escaping mechanism. In proper programming and scripting languages the syntax for double quotes is usually the same as for printf.&lt;&#x2F;p&gt;
&lt;p&gt;The shell being a bit different you should use either &lt;code&gt;printf&lt;&#x2F;code&gt; or &lt;code&gt;echo -e&lt;&#x2F;code&gt;. I have a personal preference for &lt;code&gt;printf&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-text-color-and-effects&quot;&gt;Setting Text Color and Effects&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ll focus on Select Graphic Rendition (SGR) codes here, &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ANSI_escape_code#CSIsection&quot;&gt;Wikipedia has a more complete list&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example for the shell which prints out &lt;q&gt;_FOOBAR_&lt;&#x2F;q&gt; in bright (&lt;code&gt;01&lt;&#x2F;code&gt;) red (&lt;code&gt;31&lt;&#x2F;code&gt;). After that, it resets to default settings (&lt;code&gt;00&lt;&#x2F;code&gt;) and prints a newline character (&lt;code&gt;\n&lt;&#x2F;code&gt;)&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;\033[01;31m_FOOBAR_\x1b[00m\n&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; With %s placeholder&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;\033[01;31m%s\x1b[00m\n&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;_FOOBAR_&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;As you can see in the example, multiple escape-codes can be chained in one sequence by delimiting them with a &lt;code&gt;;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;\033&lt;&#x2F;code&gt; part will turn into an ASCII escape character, the &lt;code&gt;[&lt;&#x2F;code&gt; tells the terminal that this is an ANSI Control Sequence Initiator (CSI) after that can come one or more codes. The sequence is finished by an &lt;code&gt;m&lt;&#x2F;code&gt; to tell the terminal that everything between the &lt;abbr title=&quot;Control Sequence Initiator&quot;&gt;CSI&lt;&#x2F;abbr&gt; and it are &lt;abbr title=&quot;Select Graphic Rendition&quot;&gt;SGR&lt;&#x2F;abbr&gt; codes.&lt;&#x2F;p&gt;
&lt;p&gt;!warning: &lt;b&gt;If you are using escape sequences in your scripts:&lt;&#x2F;b&gt; Please make sure that no information gets lost when those sequences are stripped, People with processing pipelines and screenreaders will thank (or at least not curse at) you.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on the &lt;code&gt;\033&lt;&#x2F;code&gt; and compatibility:&lt;&#x2F;b&gt; The &lt;code&gt;\033&lt;&#x2F;code&gt; is the octal representation of the Escape ASCII-Character and is the most widely supported one across &lt;code&gt;printf&lt;&#x2F;code&gt; implementations. (The dash printf implementation for example doesn&#x27;t support hexadecimal representation.)&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;caption&gt;Most commonly used codes&lt;&#x2F;caption&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;&#x2F;th&gt;
&lt;th&gt;Code to reverse&lt;&#x2F;th&gt;
&lt;th&gt;Effect&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;00&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;Reset all effects&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;3&quot; class=&quot;table-subheading&quot;&gt;Foreground color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;30-37&lt;&#x2F;td&gt;&lt;td&gt;39&lt;&#x2F;td&gt;&lt;td&gt;Set the Foreground color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;90-97&lt;&#x2F;td&gt;&lt;td&gt;39&lt;&#x2F;td&gt;&lt;td&gt;Set the Foreground color, bright variation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;3&quot; class=&quot;table-subheading&quot;&gt;Background color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;40-47&lt;&#x2F;td&gt;&lt;td&gt;49&lt;&#x2F;td&gt;&lt;td&gt;Set the Background color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;100-107&lt;&#x2F;td&gt;&lt;td&gt;49&lt;&#x2F;td&gt;&lt;td&gt;Set the Background color, bright variation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;3&quot; class=&quot;table-subheading&quot;&gt;Font effects&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;01&lt;&#x2F;td&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td&gt;Bright text (usually bold and brighter color)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;02&lt;&#x2F;td&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td&gt;Dim text (usually only effect on color)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;03&lt;&#x2F;td&gt;&lt;td&gt;23&lt;&#x2F;td&gt;&lt;td&gt;Italic text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;04&lt;&#x2F;td&gt;&lt;td&gt;24&lt;&#x2F;td&gt;&lt;td&gt;Underlined text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;05&lt;&#x2F;td&gt;&lt;td&gt;25&lt;&#x2F;td&gt;&lt;td&gt;Blinking text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;07&lt;&#x2F;td&gt;&lt;td&gt;27&lt;&#x2F;td&gt;&lt;td&gt;Reverse forground and background color&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;08&lt;&#x2F;td&gt;&lt;td&gt;28&lt;&#x2F;td&gt;&lt;td&gt;Concealed&#x2F;Hidden text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;09&lt;&#x2F;td&gt;&lt;td&gt;29&lt;&#x2F;td&gt;&lt;td&gt;Strikethrough text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The leading zeros are not needed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;colors&quot;&gt;Colors&lt;&#x2F;h3&gt;
&lt;p&gt;You may have noticed that the &lt;code&gt;3x&lt;&#x2F;code&gt;, &lt;code&gt;4x&lt;&#x2F;code&gt;, &lt;code&gt;9x&lt;&#x2F;code&gt;, and &lt;code&gt;10x&lt;&#x2F;code&gt; family of codes all use the last digit in the range from 0 to 7 as a color parameter.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;caption&gt;Table of colors&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Code&lt;&#x2F;th&gt;&lt;th&gt;Color&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;Black or Dark Grey&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;Red&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;Green&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;Yellow&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;Blue&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;Violet &#x2F; Purple&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td&gt;Cyan or Turquoise&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td&gt;Light Grey or White&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Please keep in mind that diffrent people have diffrent color palettes when you want to write reusable scripts.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Shell Null Termination &#x2F; Separation</title>
        <published>2023-05-27T00:00:00+00:00</published>
        <updated>2023-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/shell-null-termination/"/>
        <id>https://slatecave.net/notebook/shell-null-termination/</id>
        
        <summary type="text">What is null termination, why and how to use it in shell pipelines.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/shell-null-termination/">&lt;p&gt;Null or zero termination or separation is when one uses a null byte to separate records, which is different from the newline usually used in shell pipelines.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why&lt;&#x2F;h2&gt;
&lt;p&gt;Usually operating things by a newline is good enough and easy to debug, but even a little shell pipeline like a &lt;code&gt;ls | sort&lt;&#x2F;code&gt; can blow up when it encounters unexpected data like a filename that contains a newline character.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Fun fact:&lt;&#x2F;b&gt; Most filesystems actually allow you to use all kinds of characters from emoji to non-printables and control characters. Including newlines. One could even put ASCII-art in a filename (I&#x27;ve already done that)!&lt;&#x2F;p&gt;
&lt;p&gt;Using a null character saves you there as it is the only character guaranteed to never occur inside a filename and this becomes more important with other sources of (untrusted) input.&lt;&#x2F;p&gt;
&lt;p&gt;The improved version would be &lt;code&gt;ls --zero | sort -z&lt;&#x2F;code&gt; and a &lt;code&gt;| tr &#x27;\0&#x27; &#x27;\n&#x27;&lt;&#x2F;code&gt; to convert to newlines for the human that still wants the illusion of line separation.&lt;&#x2F;p&gt;
&lt;p&gt;Examples of inputs you shouldn&#x27;t trust:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The human in front of the machine (I know myself)&lt;&#x2F;li&gt;
&lt;li&gt;Your filesystem (because USB sticks, downloads, the human, etc.)&lt;&#x2F;li&gt;
&lt;li&gt;Anything that comes in over the network (Not even your own service, not the service you are paying for, …)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;translating-between-worlds&quot;&gt;Translating between worlds&lt;&#x2F;h2&gt;
&lt;p&gt;To translate between newline separated and the null separated world there is a little utility called &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tr.1&quot;&gt;&lt;code&gt;tr&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		null to newline
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;tr &#x27;\0&#x27; &#x27;\n&#x27;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		newline to null
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;tr &#x27;\n&#x27; &#x27;\0&#x27;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		swap newline and null
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;tr &#x27;\n\0&#x27; &#x27;\0\n&#x27;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		remove potential nulls
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;tr -d &#x27;\0&#x27;&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Make sure to actually remove the characters you assume to not be in your input!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-a-program-into-null-mode&quot;&gt;Getting a program into null mode&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately there is no &amp;quot;the one way&amp;quot; to make a program use nulls instead of newlines so here is a a hopefully useful table. Please note that some programs only have the option available in the gnu version, but not in i.e. the busybox version.&lt;&#x2F;p&gt;
&lt;p&gt;If a program has some kind of &lt;code&gt;printf&lt;&#x2F;code&gt; option one can use that to make the output null separated.&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;&#x2F;th&gt;&lt;th&gt;Version&lt;&#x2F;th&gt;&lt;th&gt;Input Flag(s)&lt;&#x2F;th&gt;&lt;th&gt;Output Flag(s)&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;awk&lt;&#x2F;td&gt;&lt;td&gt;gnu, other modern&lt;&#x2F;td&gt;&lt;td&gt;-v &#x27;RS=\0&#x27;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;cut&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;find&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;-print0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;fzf&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;--read0&lt;&#x2F;td&gt;&lt;td&gt;--print0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;grep -r&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;-Z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;grep&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;inotifywait&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;--format &#x27;…%0&#x27; --no-newline&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ls&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;--zero&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rg&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;-0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;sed&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;sort&lt;&#x2F;td&gt;&lt;td&gt;gnu, busybox&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;tar -T&lt;&#x2F;td&gt;&lt;td&gt;gnu&lt;&#x2F;td&gt;&lt;td&gt;--null&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;uniq&lt;&#x2F;td&gt;&lt;td&gt;gnu, busybox&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;td&gt;-z&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;xargs&lt;&#x2F;td&gt;&lt;td&gt;gnu, busybox&lt;&#x2F;td&gt;&lt;td&gt;-0&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;read&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;-r -d &quot;&quot;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The best way to automatically find out if a program supports an option is to &lt;code&gt;grep -q&lt;&#x2F;code&gt; across the output of its &lt;code&gt;--help&lt;&#x2F;code&gt;. Just make sure to choose a specific enough regex to avoid false positives. Trying out is also an option but that usually is a bit more complex.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Speech Recognition Frontends</title>
        <published>2023-05-09T00:00:00+00:00</published>
        <updated>2023-05-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/speech-recognition-frontends/"/>
        <id>https://slatecave.net/notebook/speech-recognition-frontends/</id>
        
        <summary type="text">Projects making use of speech recognition</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/speech-recognition-frontends/">&lt;p&gt;This page intends to give an overview what kind of projects out there are open source (or preferably free software) making use of speech recognition without relying on some well known cloud providers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;The projects listed here fall into 3 broader categories.&lt;&#x2F;p&gt;
&lt;p&gt;Build your own voice something:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#voice2json&quot;&gt;voice2json&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#dragonfly&quot;&gt;Dragonfly&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Voice Input Methods that could replace Mouse and Keyboard:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#numen&quot;&gt;Numen&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#simon-kde&quot;&gt;Simon&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Voice assistants:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#rhasspy-voice-assistant&quot;&gt;Rhasspy&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;speech-recognition-frontends&#x2F;#mycroft-voice-assistant&quot;&gt;Mycroft&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;voice2json&quot;&gt;voice2json&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;voice2json&lt;&#x2F;code&gt; is a collection of &lt;a href=&quot;https:&#x2F;&#x2F;voice2json.org&#x2F;commands.html&quot;&gt;command-line tools&lt;&#x2F;a&gt; for offline speech&#x2F;intent recognition on Linux. It is free, open source (&lt;a href=&quot;https:&#x2F;&#x2F;opensource.org&#x2F;licenses&#x2F;MIT&quot;&gt;MIT&lt;&#x2F;a&gt;), and &lt;a href=&quot;https:&#x2F;&#x2F;voice2json.org&#x2F;#supported-languages&quot;&gt;supports 18 human languages&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;voice2json.org&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;voice2json.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;It can make use of &lt;a href=&quot;https:&#x2F;&#x2F;kaldi-asr.org&#x2F;&quot;&gt;CMU Sphinx&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;kaldi-asr.org&#x2F;&quot;&gt;Kaldi&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;DeepSpeech&quot;&gt;Mozilla DeepSpeech&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;julius-speech&#x2F;julius&quot;&gt;Julius&lt;&#x2F;a&gt;, depending on the used language profile.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dragonfly&quot;&gt;Dragonfly&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Dragonfly is a speech recognition framework for Python that makes it convenient to create custom commands to use with speech recognition software.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;dictation-toolbox&amp;#x2F;dragonfly&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;dictation-toolbox&amp;#x2F;dragonfly
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;I learned about this from &lt;a href=&quot;https:&#x2F;&#x2F;handsfree.dev&quot;&gt;handsfree.dev&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;numen&quot;&gt;Numen&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Numen is voice control for computing without a keyboard. It works system-wide on Linux and the speech recognition runs locally.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;numenvoice.org&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;numenvoice.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;johngebbie.com&#x2F;&quot;&gt;John Gebbie&lt;&#x2F;a&gt; also has some &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~geb&#x2F;numen#see-also&quot;&gt;related projects linked in the README&lt;&#x2F;a&gt;, he also made &lt;a href=&quot;https:&#x2F;&#x2F;handsfree.dev&#x2F;&quot;&gt;handsfree.dev&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simon-kde&quot;&gt;Simon (KDE)&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Simon is an open source speech recognition program that can replace your mouse and keyboard. The system is designed to be as flexible as possible and will work with any language or dialect.&lt;&#x2F;p&gt;
&lt;p&gt;Simon uses the &lt;a href=&quot;https:&#x2F;&#x2F;kde.org&quot;&gt;KDE libraries&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;cmusphinx.github.io&#x2F;&quot;&gt;CMU SPHINX&lt;&#x2F;a&gt; and &#x2F; or &lt;a href=&quot;https:&#x2F;&#x2F;julius.osdn.jp&#x2F;en_index.php&quot;&gt;Julius&lt;&#x2F;a&gt; coupled with the &lt;a href=&quot;https:&#x2F;&#x2F;htk.eng.cam.ac.uk&#x2F;&quot;&gt;HTK&lt;&#x2F;a&gt; and runs on Windows and Linux.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;simon.kde.org&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;simon.kde.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt;Some links have been replaced with their redirect targets.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rhasspy-voice-assistant&quot;&gt;Rhasspy Voice Assistant&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Rhasspy (ɹˈæspi) is an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rhasspy&quot;&gt;open source&lt;&#x2F;a&gt;, fully offline set of &lt;a href=&quot;https:&#x2F;&#x2F;rhasspy.readthedocs.io&#x2F;en&#x2F;latest&#x2F;#services&quot;&gt;voice assistant services&lt;&#x2F;a&gt; for many human languages […]&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;rhasspy.readthedocs.io&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;rhasspy.readthedocs.io
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mycroft-voice-assistant&quot;&gt;Mycroft Voice Assistant&lt;&#x2F;h2&gt;
&lt;p&gt;Mycroft is an open source voice Assistant that makes use of a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;MycroftAI&#x2F;mycroft-precise&quot;&gt;custom wakeword listener&lt;&#x2F;a&gt; and Mozilla &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;DeepSpeech&quot;&gt;DeepSpeech&lt;&#x2F;a&gt; together with some custom components that has no easy-to-find non-marketing short description.&lt;&#x2F;p&gt;
&lt;p&gt;If you are looking for the technical stuff instead of company selling things stuff, you&#x27;ll find it in the Documentation menu of their page.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;mycroft.ai&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;mycroft.ai&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;feedback&quot;&gt;Feedback&lt;&#x2F;h2&gt;
&lt;p&gt;If some of the information here is outdated, incorrect or you know a project that is not in this list please &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;site-source.slatecave-net&#x2F;issues&quot;&gt;open an issue over on Codeberg&lt;&#x2F;a&gt; or &lt;a href=&quot;&#x2F;about&#x2F;me#contact&quot;&gt;contact me in some other way&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Scanning, OCR and PDF</title>
        <published>2023-04-24T00:00:00+00:00</published>
        <updated>2023-04-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/scanning-ocr-and-pdf/"/>
        <id>https://slatecave.net/notebook/scanning-ocr-and-pdf/</id>
        
        <summary type="text">A cheatsheet for scanning and and OCRing documents on Linux with sane and tesseract</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/scanning-ocr-and-pdf/">&lt;h2 id=&quot;scanning&quot;&gt;Scanning&lt;&#x2F;h2&gt;
&lt;p&gt;Under Linux one can scan an image with the &lt;code&gt;scanimage&lt;&#x2F;code&gt; command provided by the &lt;a href=&quot;http:&#x2F;&#x2F;sane-project.org&#x2F;&quot;&gt;SANE framework&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A helper function that scans an image from the default scanner to the given path with settings suitable for OCR&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function z-shell&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-shell&quot;&gt;scan_image_to&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;Scanning Image …\n&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;scanimage&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;mode&lt;&#x2F;span&gt; Gray&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;format&lt;&#x2F;span&gt; png&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;resolution&lt;&#x2F;span&gt; 300&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;o&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;p&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;unpaper&quot;&gt;Unpaper&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;unpaper&#x2F;unpaper&quot;&gt;Unpaper&lt;&#x2F;a&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;unpaper&quot;&gt;manpage&lt;&#x2F;a&gt;) is a scan post-processing tool which can remove paper and scan artefacts and also do alignment of carelessly scanned pages. Which is great when preparing them for OCR.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to a friend of mine for mentioning it! &lt;&#x2F;p&gt;
&lt;h2 id=&quot;ocr-using-tesseract-and-imagemagick&quot;&gt;OCR using Tesseract and imagemagick&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tesseract-ocr&#x2F;tesseract&quot;&gt;Tesseract&lt;&#x2F;a&gt; is both, a library and &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tesseract&quot;&gt;command line tool&lt;&#x2F;a&gt; for Optical Character Recognition (OCR). Image in, text in image out. &lt;&#x2F;p&gt;
&lt;p&gt;It best works with greyscale images with low background noise and high contrast. Getting a scan closer to that can be done using unpaper or - for scans of known quality - imagemagick using its builtin contrast filter and the level clipping, which is faster than any fully automatic tool.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A function that can be used to OCR a given prerotated image that does some preprocessing with imagemagick to improve noise (the -level option) and contrast for scans, the output is the OCR as plaintext to stdout.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Usage: ocr_image &amp;lt;image&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-shell&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-shell&quot;&gt;ocr_image&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;convert&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;colorspace&lt;&#x2F;span&gt; Gray&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;contrast&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;level&lt;&#x2F;span&gt; 20&lt;span class=&quot;z-meta z-group z-expansion z-job z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-job z-shell&quot;&gt;%&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;,100&lt;span class=&quot;z-meta z-group z-expansion z-job z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-job z-shell&quot;&gt;%&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;contrast&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;contrast&lt;&#x2F;span&gt; -&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;tesseract&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;l&lt;&#x2F;span&gt; deu+eng&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;oem&lt;&#x2F;span&gt; 2&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;psm&lt;&#x2F;span&gt; 3 - -&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;To explain the most important tesseract options:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		-l
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		To set the used &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tesseract#LANGUAGES_AND_SCRIPTS&quot;&gt;language or script&lt;&#x2F;a&gt;. Accepts multiple values separated by a &lt;code&gt;+&lt;&#x2F;code&gt;. i.e. &lt;code&gt;-l deu+eng&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		--oem
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		To set the mode of operation, meaning whether or not to use the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Long_short-term_memory&quot;&gt;LSTM&lt;&#x2F;a&gt; based approach or the original tesseract OCR. At the time of writing I had the best results with option 2, enabling both.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		--psm
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Tells tesseract what &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tesseract#OPTIONS&quot;&gt;kind of layout analysis&lt;&#x2F;a&gt; to run. I prefer to do a separate OSD (Orientation and Script Detection) only run in mode 0 rotating the image and then using the full automatic layout mode 3 without OSD to do the actual OCR.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		--user-words
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		To give tesseract a text file with a list of words that are likely to occur in the document, this helps avoiding the OCR equivalent to a typo. There is also a pattern file (note that these are &lt;b&gt;not&lt;&#x2F;b&gt; regexes!). &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;tesseract#CONFIG_FILES_AND_AUGMENTING_WITH_USER_DATA&quot;&gt;More on word and pattern lists in the tesseract manpage.&lt;&#x2F;a&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;figure&gt;
	&lt;figcaption&gt;This function uses tesseract with the psm 0 mode to correctly orientate a given image file that is assumed to be German or English text. The file is then replaced.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Usage: rotate_scanned_image &amp;lt;image&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-shell&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-shell&quot;&gt;rotate_scanned_image&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Replace or leve out the -l option here if you work with other languages&#x2F;scripts&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;PAGE_OSD&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-command z-parens z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;tesseract&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;l&lt;&#x2F;span&gt; deu+eng&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;oem&lt;&#x2F;span&gt; 2&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;psm&lt;&#x2F;span&gt; 0 &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; - &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-file-descriptor z-shell&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-file-descriptor z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;if DEBUG_OSD is set print some debug output.&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-support z-function z-test z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;-&lt;&#x2F;span&gt;n&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;DEBUG_OSD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-test z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-and z-shell&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;PAGE OSD =====\n%s\n==============\n&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;PAGE_OSD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-file-descriptor z-shell&quot;&gt;2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;APPLY_ROTATION&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-command z-parens z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;%s\n&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;PAGE_OSD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;awk&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&#x2F;^Rotate:&#x2F;{ print $2 }&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;convert&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;rotate&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;APPLY_ROTATION&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;:-&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Tesseract assumes the images to be scanned at 300dpi, if you are using a different resolution make sure to tell tesseract using the &lt;code&gt;--dpi&lt;&#x2F;code&gt; option.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pdf-creation-and-manipulation&quot;&gt;PDF creation and manipulation&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;creating-a-pdf-from-images&quot;&gt;Creating a PDF from images&lt;&#x2F;h3&gt;
&lt;p&gt;To create a PDF from images without OCR one can use imagemagick.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example that uses the &lt;code&gt;convert&lt;&#x2F;code&gt; command to convert two png files to an pdf with a pagesize of DIN A4.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;convert&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;page&lt;&#x2F;span&gt; a4 page_1.png page_2.png document.pdf&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;Also see the &lt;a href=&quot;https:&#x2F;&#x2F;imagemagick.org&#x2F;script&#x2F;command-line-options.php#page&quot;&gt;imagemagick documentation on the -page option.&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ocr-using-ocrmypdf&quot;&gt;OCR using OCRmyPDF&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;ocrmypdf.readthedocs.io&#x2F;en&#x2F;latest&#x2F;index.html&quot;&gt;OCRmyPDF&lt;&#x2F;a&gt; is a python wrapper for tesseract that makes it pretty painless to run OCR on a PDF and turn PDFs into &lt;a href=&quot;https:&#x2F;&#x2F;ocrmypdf.readthedocs.io&#x2F;en&#x2F;latest&#x2F;introduction.html#about-pdf-a&quot;&gt;standards compliant PDF&#x2F;A&lt;&#x2F;a&gt; and optimize PDFs to reduce filesize.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example for OCRing a PDF file that contains German and English text as images.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;ocrmypdf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;l&lt;&#x2F;span&gt; deu+eng input.pdf output.pdf&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The package is called &lt;code&gt;ocrmypdf&lt;&#x2F;code&gt; for all major Linux distributions (AUR on Arch).&lt;&#x2F;p&gt;
&lt;p&gt;Thank you (again) to the friend who found this tool.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-pdfs-with-ocr-using-tesseract-and-poppler&quot;&gt;Creating PDFs with OCR using tesseract and poppler&lt;&#x2F;h3&gt;
&lt;p&gt;To create PDFs with an OCR overlay one can delegate the image to PDF step to tesseract using its &lt;code&gt;pdf&lt;&#x2F;code&gt; preset and join the resulting documents after the OCR step with the poppler &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pdfunite&quot;&gt;&lt;code&gt;pdfunite&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; tool.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Tesseract command to generate a PDF with OCR overlay for a single page with the options left out (they are the same as with the plaintext preset)&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;tesseract page_n.png page_n [options...] pdf
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;figure&gt;
	&lt;figcaption&gt;pdfunite command to join multiple PDFs together. Btw. these can be any PDF documents.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;pdfunite&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; page_&lt;span class=&quot;z-keyword z-operator z-regexp z-quantifier z-shell&quot;&gt;*&lt;&#x2F;span&gt;.pdf document.pdf&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; These OCR overlays while pretty good are intended for making the documents searchable, they are not a replacement for proper semantic and accessible documents.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-text-out-of-pdf-files&quot;&gt;Getting text out of PDF files&lt;&#x2F;h3&gt;
&lt;p&gt;To get text out of a PDF one can use the &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pdftotext&quot;&gt;&lt;code&gt;pdftotext&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pdftohtml&quot;&gt;&lt;code&gt;pdftohtml&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; command (which come with poppler).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;pdftotext&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; file.pdf&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;packages&quot;&gt;Packages&lt;&#x2F;h2&gt;
&lt;p&gt;To get the commandline utilities on some distributions one should also install the &lt;code&gt;&amp;lt;packagename&amp;gt;-bin&lt;&#x2F;code&gt; package.&lt;&#x2F;p&gt;
&lt;p&gt;Imagemagick is sometimes just called &lt;code&gt;magick&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Tesseract usually comes without the needed data files, these probably come in packages called like &lt;code&gt;tesseract-data-&amp;lt;lang&amp;gt;&lt;&#x2F;code&gt;, if you just want everything there is probably a &lt;code&gt;tesseract-data-all&lt;&#x2F;code&gt; metapackage.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;pdfunite&lt;&#x2F;code&gt; and &lt;code&gt;pdfto*&lt;&#x2F;code&gt; commands are either directly in the &lt;code&gt;poppler&lt;&#x2F;code&gt; package or in &lt;code&gt;poppler-utils&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Slatectx: Adding some context to the desktop</title>
        <published>2023-04-23T00:00:00+00:00</published>
        <updated>2023-04-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/slatectx-why-and-how/"/>
        <id>https://slatecave.net/blog/slatectx-why-and-how/</id>
        
        <summary type="text">Most workflows today are app-centric, this is my attempt at trying something different</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/slatectx-why-and-how/">&lt;h2 id=&quot;why-i-created-slatectx&quot;&gt;Why I created slatectx&lt;&#x2F;h2&gt;
&lt;p&gt;To put my usecase and workflow in a few sentences:&lt;&#x2F;p&gt;
&lt;p&gt;What I&#x27;m mostly doing is editing textfiles, writing code, configuration, notes and so on.
I&#x27;m doing a lot of thing in the shell (bash) and my text editor (&lt;a href=&quot;https:&#x2F;&#x2F;kakoune.org&quot;&gt;kakoune&lt;&#x2F;a&gt;), usually working with more than one window open using i3 or sway as a window manager. Leading to …&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I want a new terminal and I want it in the same pwd as the last terminal I interacted with on that workspace!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The workspace constraint is because I sometimes switch between a reference or otherwise related project to the main thing I&#x27;m working on and I don&#x27;t want the pwd on one to influence the pwd on the other and usually I already separate those by putting them onto a different workspace.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;identifying-contexts&quot;&gt;Identifying Contexts&lt;&#x2F;h2&gt;
&lt;p&gt;To be able to do anything that is context aware, one needs a way to identify a certain context and a way of finding out which context currently applies. Since this journey started with a simple POC and then stayed pretty simple slatectx contexts are named by opaque (no assumptions based on the name) strings containing alphanumeric characters, dashes &lt;code&gt;-&lt;&#x2F;code&gt; and underscores &lt;code&gt;_&lt;&#x2F;code&gt; with slashes &lt;code&gt;\&lt;&#x2F;code&gt; being forbidden because these have to work in filenames.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generating-context-names&quot;&gt;Generating Context Names&lt;&#x2F;h3&gt;
&lt;p&gt;With the requirements being that I don&#x27;t want an additional daemon for doing housekeeping and be able to debug where the names are coming from I settled on the following rough naming scheme:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;lt;session_type&amp;gt;&amp;lt;sesion_id_or_display&amp;gt;-&amp;lt;workspace&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;b&gt;For example,&lt;&#x2F;b&gt; the workspace I&#x27;m writing this post on maps to the context &lt;code&gt;y1-9&lt;&#x2F;code&gt;, derived from me currently using a wayland session that has &lt;code&gt;wayland-1&lt;&#x2F;code&gt; set as its &lt;code&gt;WAYLAND_DISPLAY&lt;&#x2F;code&gt; and being on workspace 1. With an ssh connection this would simply be &lt;code&gt;ssh&lt;&#x2F;code&gt; and tmux uses &lt;code&gt;t-&amp;lt;tmux_pid&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To generate those identifiers my approach involved &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;slatectx-get-context-name&quot;&gt;finding out which session type is currently used&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;slatectx-get-workspace&quot;&gt;getting the workspace&lt;&#x2F;a&gt; and then stitching that together in the &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;slatectx-get-context-name&quot;&gt;&lt;code&gt;slatectx-get-context-name&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; which is the central interface for getting the name of the current context (A bit like xdg-open for opening files).&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;slatectx-get-context-name&lt;&#x2F;code&gt; script is supposed to be replaced when one wishes to use a different context naming logic.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;switching-directories&quot;&gt;Switching directories&lt;&#x2F;h2&gt;
&lt;p&gt;With the problem of figuring out the current context out solved one can implement things like a mot recently used directory (the original motivation). The logic behind that has now moved to its own tool &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;slatectx-pwd&quot;&gt;&lt;code&gt;slatectx-pwd&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;When used with the &lt;i&gt;set&lt;&#x2F;i&gt; command it validates that the directory exists (and is a directory), turns a relative into an absolute path and stores the result in &lt;code&gt;$XDG_RUNTIME_DIR&#x2F;slatectx-pwd&#x2F;$(slatectx-get-context-name)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;i&gt;get&lt;&#x2F;i&gt; operation reads this file, runs the checks again (the filesystem could have been modified in between) and prints the path out, depending on the check results with an error return code.&lt;&#x2F;p&gt;
&lt;p&gt;This little helper is used in &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.bashrc&quot;&gt;my bashrc&lt;&#x2F;a&gt; to &lt;code&gt;cd&lt;&#x2F;code&gt; into the last used directory on startup and hooked into the &lt;code&gt;PROMPT_COMMAND&lt;&#x2F;code&gt; to record the last used directory.&lt;&#x2F;p&gt;
&lt;p&gt;A similar thing happens with a &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;filedialog&quot;&gt;filedialog script&lt;&#x2F;a&gt; I&#x27;m using to quickly navigate the filesystem and open files, which on startup gets passed the result of &lt;code&gt;slatectx-pwd get&lt;&#x2F;code&gt; and has a hook to set the pwd on a change-directory event.&lt;&#x2F;p&gt;
&lt;p&gt;Another thing that is set up in my shell and text-editor is a &lt;code&gt;cdx&lt;&#x2F;code&gt; alias for quickly jumping to the slatectx-pwd in an already open session.&lt;&#x2F;p&gt;
&lt;p&gt;There is also experimental configuration for &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.config&#x2F;lf&#x2F;lfrc&quot;&gt;integrating it with lf&lt;&#x2F;a&gt; which I don&#x27;t use as often as I&#x27;d like to, because I haven&#x27;t figured out how to make use of lf in a way that makes me not run into a wall at some point where a command I expect isn&#x27;t there because I haven&#x27;t figured out how to configure that part of lf yet. (one of the reasons I wrote the filedialog script)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shared-text-editor&quot;&gt;Shared text-editor&lt;&#x2F;h2&gt;
&lt;p&gt;One thing I missed after switching to kakoune from gedit was that with gedit I could just have a keybind that opened another text editing window and I could just move the tabs between them (not missing the tab UI though).&lt;&#x2F;p&gt;
&lt;p&gt;With kakoune having a client-server architecture one could replicate having multiple clients share the same open buffers, if only there was a way to find out what the server is call … yes one can tell kakoune what the socket should be called and whether it should start in client or server mode. That&#x27;s what the &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;kak-slatectx&quot;&gt;&lt;code&gt;kak-slatectx&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and the &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;kak-e&quot;&gt;&lt;code&gt;kak-e&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; are for.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kak-slatectx&quot;&gt;kak-slatectx&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;kak-slatectx&lt;&#x2F;code&gt; replaces the &lt;code&gt;kak&lt;&#x2F;code&gt; command (using an alias) and by default tells kakoune to start with the given slatectx context as the socket name in client or server mode depending on the presence of the socket file. To make it a full replacement, this behaviour is completely disabled, should one attempt to pass the &lt;code&gt;-c&lt;&#x2F;code&gt; or &lt;code&gt;-s&lt;&#x2F;code&gt; options to kakoune.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kak-e&quot;&gt;kak-e&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;kak-e&lt;&#x2F;code&gt; is the slatectx enabled fileopener for kakoune, for it to work kakoune has to remember the last window one interacted with, which is a &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.config&#x2F;kak&#x2F;autoload&#x2F;current-session.kak&quot;&gt;pretty simple &lt;code&gt;FocusIn&lt;&#x2F;code&gt; hook paired with a custom &lt;code&gt;current_client&lt;&#x2F;code&gt; option&lt;&#x2F;a&gt;. &lt;code&gt;kak-e&lt;&#x2F;code&gt; constructs an edit command for kakoune that then opens the given files in the last focused window, launching a new kakoune instance if needed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reusing-an-image-viewer&quot;&gt;Reusing an image viewer&lt;&#x2F;h2&gt;
&lt;p&gt;Another thing I set up a wrapper for is &lt;a href=&quot;https:&#x2F;&#x2F;sr.ht&#x2F;~exec64&#x2F;imv&#x2F;&quot;&gt;imv&lt;&#x2F;a&gt; (which at the time of writing is looking for a new maintainer!), an image viewer which also has a socket for remote controlling it. Since imv always uses its pid for this, an approach similar to slatectx-pwd is used to store the pid of the currently running imv instance and then using that like &lt;code&gt;kak-e&lt;&#x2F;code&gt; to open files in an already open window (Which currently is a bit glitchy).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;imv-slatectx&quot;&gt;&lt;code&gt;imv-slatectx&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; also has an option to disable launching an imv instance, which is useful for preview displays. Open an Image, look at it select the next and the image viewer automatically jumps to the selected image. Close the image viewer and the previews are gone with more screen real estate for managing files. (I&#x27;ve integrated it like this into my lf configuration and it is pretty useful)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Here I described why and how I used some relatively simple shellscript to implement some shared pieces of information that can be used to &lt;b&gt;make applications work together.&lt;&#x2F;b&gt; Something I miss with most GUI applications that maybe have a hared recently used, but completely ignore what the one using them is currently working on.&lt;&#x2F;p&gt;
&lt;p&gt;That said: Feel free to to reuse my code and ideas on this one and nag me on writing documentation and packaging if you are interested.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>What is NAT and why you don&#x27;t want it</title>
        <published>2023-03-27T00:00:00+00:00</published>
        <updated>2023-03-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/nat/"/>
        <id>https://slatecave.net/blog/nat/</id>
        
        <summary type="text">What is Network Address Translation, how does it work and why you don&#x27;t want it.</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/nat/">&lt;p&gt;You may or may not have heard of NAT, Network Address Translation, here is how it works, why you don&#x27;t want it and why you probably have to use it anyway.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-the-internet-was-supposed-to-work&quot;&gt;How the Internet was supposed to work&lt;&#x2F;h2&gt;
&lt;p&gt;The original idea of the internet was that every machine is reachable, can directly connect to other machines using nothing but their public address and also offer serveries. Which worked pretty well until …&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-the-internet-community-started-nating&quot;&gt;Why the Internet community started NATing&lt;&#x2F;h3&gt;
&lt;p&gt;… the Internet started running out of publicly reachable addresses. So everyone started assigning private IP-Addresses to their internal networks, trying to get the most out of the precious public address space.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;legacy-nat&quot;&gt;Legacy NAT&lt;&#x2F;h2&gt;
&lt;p&gt;Legacy NAT is simply swapping one IP-Address for another, with a 1:1 relationship between private and public addresses. Either statically assigned or on demand to a machine that wants to connect, so if you wanted to connect to the Internet you have to wait for a free Line (&lt;i&gt;public Address&lt;&#x2F;i&gt;) from a pool of available addresses (called a &lt;i&gt;NAT Pool&lt;&#x2F;i&gt;). Hello old dialup!&lt;&#x2F;p&gt;
&lt;p&gt;These are respectively called Static and Dynamic NAT.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;modern-nat-port-address-translation-pat&quot;&gt;Modern NAT - Port Address Translation (PAT)&lt;&#x2F;h2&gt;
&lt;p&gt;Nowadays with 10s of devices per household with active connections to outside servers that Legacy approach of give a machine an IP when it needs one and recycle after doesn&#x27;t work anymore.&lt;&#x2F;p&gt;
&lt;p&gt;But one can abuse the fact that most machines don&#x27;t offer services for the public and therefore have a lot of outgoing and no incoming connections. So instead of assigning whole IP-Addresses we made our Routers translate combinations of Layer 4 ports and IP-Address.&lt;&#x2F;p&gt;
&lt;p&gt;If one now wants to connect to the server at &lt;code&gt;1.2.3.4&lt;&#x2F;code&gt; on port 443 from &lt;code&gt;192.168.0.10&lt;&#x2F;code&gt; from port 12345 via a router assigned &lt;code&gt;203.0.113.3&lt;&#x2F;code&gt; (it would with multiple public addresses, too) as its public address  …&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The router tries to find a port on its public address using either the original source port or if already in use because someone else behind the NAT had the same idea of a great source port a port &lt;b&gt;near&lt;&#x2F;b&gt; the original port. Let&#x27;s say we ended up with 12347.&lt;&#x2F;li&gt;
&lt;li&gt;Now the router replaces the internal address with the public one and swaps out the port for the publicly reachable one and remembers the combination until the connection closes, there is a timeout.&lt;&#x2F;li&gt;
&lt;li&gt;If now an answer arrives on &lt;code&gt;203.0.113.3&lt;&#x2F;code&gt; port 12345 the NAT knows that the real destination is &lt;code&gt;192.168.0.10&lt;&#x2F;code&gt; on port 12345 and vice versa.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For ICMP the identifiers can be used similar to port numbers. UDP based protocols need additional support from the NAT as UDP itself has no way of matching requests with replies.&lt;&#x2F;p&gt;
&lt;p&gt;With that setup one can support up to ~&lt;b&gt;60000&lt;&#x2F;b&gt; concurrent TCP connections with a single public IP-Address, great isn&#x27;t it?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;carrier-grade-nat&quot;&gt;Carrier grade NAT&lt;&#x2F;h2&gt;
&lt;p&gt;Well, your provider most probably thinks that that is great because now they can serve a whole lot of customers and still only pay for a handful of public IPs. There is even the &lt;code&gt;100.64.0.0&#x2F;10&lt;&#x2F;code&gt; network reserved in &lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc6598.html&quot;&gt;RFC 6598&lt;&#x2F;a&gt; for exactly this purpose.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problems-with-nat&quot;&gt;Problems with NAT&lt;&#x2F;h2&gt;
&lt;p&gt;The problem with this is now that your average client is behind two or even more layers of NAT, one at their provider, one built into their home-router. That comes with at least two problems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;For real-time applications (Videochat,SIP) you want as direct of a connection as possible which is the opposite of what a NAT does.&lt;&#x2F;li&gt;
&lt;li&gt;Long running connections (i.e. your chat or Mail-client waiting for realtime notifications or an ssh session) may get terminated because the NAT needs free ports because 60000 connections is surprisingly little scaling up.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Also NAT &lt;em&gt;actively hinders innovation&lt;&#x2F;em&gt;. Ever heared of SCTP, the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stream_Control_Transmission_Protocol&quot;&gt;Stream Control Transmission Protocol&lt;&#x2F;a&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc9260.html&quot;&gt;RFC 9260&lt;&#x2F;a&gt;)? One of the reasons you have probably never seen it in the wild is because old NATs (like Joe Averages router running outdated firmware) have no clue what to do with it. Same problem with  &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;QUIC&quot;&gt;QUIC&lt;&#x2F;a&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc9000.html&quot;&gt;RFC 9000&lt;&#x2F;a&gt;) which is based on UDP, because punching a hole into the NAT that has the right shape is usually difficult to impossible you probably won&#x27;t find it in the wild.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nat64&quot;&gt;NAT64&lt;&#x2F;h2&gt;
&lt;p&gt;NAT64 was a way to translate IPv6 adresses in the &lt;code&gt;64:ff9b::&#x2F;96&lt;&#x2F;code&gt; network to IPv4 but it has been deprecated in favour of dualstack configurations. Probably because for that to work either direct application support is needed or a dns Server which has to translate IPv4 addresses to the NATable IPv6 ones.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;myths-around-nat&quot;&gt;Myths around NAT&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;great-i-don-t-need-a-firewall&quot;&gt;Great I don&#x27;t need a Firewall!&lt;&#x2F;h3&gt;
&lt;p&gt;Yes you do! A NAT only denies incoming connections because no machine in the internal network currently punched a hole in it with UPnP. Your NAT is also waaay too complex for trusting it with denying unwanted connections. (Don&#x27;t confuse it with a NAT built into a firewall)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-want-ipv6-nat-because-of-privacy&quot;&gt;I want IPv6 NAT because of Privacy!&lt;&#x2F;h3&gt;
&lt;p&gt;If you have this kind of network access you can as well reassign random addresses using your favourite IPv6 address assignment mechanism.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;solving-the-nat-problems&quot;&gt;Solving the NAT problems&lt;&#x2F;h2&gt;
&lt;p&gt;So you don&#x27;t like your NAT anymore? What if the Internet had an address space large enough for every device? Well its your lucky day the solution is called IPv6 and if in 2023 your ISP doesn&#x27;t give you IPv6, ask them nicely why they are stuck in the past.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Building an animated &lt;details&gt; element</title>
        <published>2023-03-17T00:00:00+00:00</published>
        <updated>2023-03-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/animated-details-element/"/>
        <id>https://slatecave.net/blog/animated-details-element/</id>
        
        <summary type="text">Trying to build an accessible accordion element for reasons</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/animated-details-element/">&lt;p&gt;Accordion elements on the web seem to be everywhere and almost everyone seems to implement them in a way that only works for the well sighted user with a modern browser with JavaScript enabled. I want to show that these can be built in a better way.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-why-are-accordions-used-at-all&quot;&gt;So why are accordions used at all?&lt;&#x2F;h2&gt;
&lt;p&gt;While using accordions or expanders in some situations is a bad idea, sometimes they help with uncluttering, hiding information that isn&#x27;t needed by most people but available from the already loaded document on request when needed.&lt;&#x2F;p&gt;
&lt;p&gt;An example would be to provide contact details on a page, usually visitors don&#x27;t care, but when they do, that information is easy to find.&lt;&#x2F;p&gt;
&lt;p&gt;Another example would be an event log with the summaries always being visible and the details being available inline on request. sourcehut and YunoHost use this pattern.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Accordions usually are multiple collapsible elements grouped together and only allow one open at a time. I&#x27;m not going into that functionality here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-html-way-of-building-an-accordion&quot;&gt;The HTML way of building an accordion …&lt;&#x2F;h2&gt;
&lt;p&gt;This pattern is so useful that HTML has elements for exactly that usecase, the &lt;code&gt;&amp;lt;details&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;&#x2F;code&gt; elements, one simply puts a summary inside a details tag along with some content and it works, in all browsers, without JavaScript, with any input method the browser supports.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Apparently &lt;a href=&quot;https:&#x2F;&#x2F;www.scottohara.me&#x2F;blog&#x2F;2022&#x2F;09&#x2F;12&#x2F;details-summary.html&quot;&gt;the details element and accessibility is it&#x27;s own can of worms, too&lt;&#x2F;a&gt;. As the article mentions: You decide.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example of such a details element&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;html&quot; class=&quot;language-html z-code&quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;z-text z-html z-basic&quot;&gt;&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Your DNS records for example.org are not configured&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Add an &lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;A&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; record pointing to &lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;93.184.216.34&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;.&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Add an &lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;AAAA&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; record pointin to &lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;2606:2800:220:1:248:1893:25c8:1946&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;It is important to set both, otherwise peopley may be unable to access your server.&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It then would be displayed similar to this:&lt;&#x2F;p&gt;
&lt;details&gt;
	&lt;summary&gt;Your DNS records for example.org are not configured&lt;&#x2F;summary&gt;
	&lt;p&gt;Add an &lt;code&gt;A&lt;&#x2F;code&gt; record pointing to &lt;code&gt;93.184.216.34&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
	&lt;p&gt;Add an &lt;code&gt;AAAA&lt;&#x2F;code&gt; record pointing to &lt;code&gt;2606:2800:220:1:248:1893:25c8:1946&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
	&lt;p&gt;It is important to set both, otherwise people may be unable to access your server.&lt;&#x2F;p&gt;
&lt;&#x2F;details&gt;
	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;what-people-are-doing-instead&quot;&gt;What people are doing instead …&lt;&#x2F;h2&gt;
&lt;p&gt;You may have been surprised by the fact that there is an HTML element for exactly this purpose because you have never seen one in the wild.&lt;&#x2F;p&gt;
&lt;p&gt;There are probably three reasons people don&#x27;t use it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They simply don&#x27;t know that it exists&lt;&#x2F;li&gt;
&lt;li&gt;They found out, tried to animate it and failed to do so&lt;&#x2F;li&gt;
&lt;li&gt;They use a library which fell victim to one of the previous reasons.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The result being that they give up and write their own accordion from scratch, button &lt;code&gt;onclick&lt;&#x2F;code&gt; handler, element with changing styles, done. Except that the result is usually an accessibility nightmare, and completely falls apart when for some reason the JavaScript needed to operate it isn&#x27;t available.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-is-the-details-element-seemingly-hard-to-animate&quot;&gt;Why is the details element seemingly hard to animate?&lt;&#x2F;h2&gt;
&lt;p&gt;Animating a details tag is simple, one can use the &lt;code&gt;[open=&amp;quot;&amp;quot;]&lt;&#x2F;code&gt; CSS selector to find out whether the details element is currently open, put some keyframes and an animation (more details later) on it and it opens with a smooth animation and closing is … not animated at all.&lt;&#x2F;p&gt;
&lt;p&gt;No your CSS isn&#x27;t at fault here.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-the-details-element-works&quot;&gt;How the details element works.&lt;&#x2F;h3&gt;
&lt;p&gt;The details element is a bit of a unicorn in that it manipulates the page tree. When the content gets shown elements are added and when it closes the elements get removed from the page instantly, and while that is great as it saves resources PR is not happy because they want an animation.&lt;&#x2F;p&gt;
&lt;p&gt;My personal guess is that this is where most developers who know about the details element give up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;animating-it-anyway&quot;&gt;Animating it Anyway&lt;&#x2F;h2&gt;
&lt;p&gt;So we want an animation for sighted people with modern browsers and a good experience for everyone else, so lets try to achieve that by using the web as it was intended, HTML for content, CSS for eye candy and JavaScript for providing some optional functionality.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Animating slightly degrades accessibility (which is probably still better than with your completely custom accordion) of the details element.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;animated-details-element-demo&quot;&gt;You can find the final result over on Codeberg.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start with the content, I&#x27;ll assume you have a plain details and summary tag filled with your content and we want to leave it as it is.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; class=&quot;language-html z-code&quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;z-text z-html z-basic&quot;&gt;&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Your DNS records for example.org are not configured&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Add an &lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;A&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; record pointing to &lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;93.184.216.34&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;.&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;Add an &lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;AAAA&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; record pointin to &lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;2606:2800:220:1:248:1893:25c8:1946&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;It is important to set both, otherwise peopley may be unable to access your server.&lt;span class=&quot;z-meta z-tag z-block z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-block z-any z-html&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-tag z-inline z-any z-html&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-begin z-html&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag z-inline z-any z-html&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-tag z-end z-html&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In case you want to follow along but only have a desktop browser: open a new tab, navigate to &lt;code&gt;about:blank&lt;&#x2F;code&gt; and open up the developer tools. Depending on the browser you now have a pretty good IDE with live preview.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-grand-opening&quot;&gt;The Grand Opening&lt;&#x2F;h3&gt;
&lt;p&gt;Before we worry about closing the element, lets worry about it opening.&lt;&#x2F;p&gt;
&lt;p&gt;The simplest way here is to simply animate the &lt;code&gt;max-height&lt;&#x2F;code&gt; property from the current height of the element (&lt;code&gt;--details-current-height&lt;&#x2F;code&gt;) to one screen height (&lt;code&gt;100vh&lt;&#x2F;code&gt;), filling only back in time in case the details element is taller than one screen height (i.e. on a smaller device), in that case it will simply snap to full height off screen and is fully viewable.&lt;&#x2F;p&gt;
&lt;p&gt;Because we currently have no idea what the current height of the details element is, falling back to an assumed &lt;code&gt;1em&lt;&#x2F;code&gt; as the height of our summary element works pretty well. (You may have to adjust this based on extra padding and margin you are applying)&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;css&quot; class=&quot;language-css z-code&quot;&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;z-source z-css&quot;&gt;&lt;span class=&quot;z-meta z-at-rule z-keyframe z-css&quot;&gt;&lt;span class=&quot;z-keyword z-control z-at-rule z-keyframe z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-keyword z-css&quot;&gt;@&lt;&#x2F;span&gt;keyframes&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-animation-name z-css&quot;&gt;details-appear&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-keyframe-selector z-css&quot;&gt;from&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-current-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; 1em&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-keyframe-selector z-css&quot;&gt;to&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-css&quot;&gt;100&lt;span class=&quot;z-keyword z-other z-unit z-css&quot;&gt;vh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-selector z-css&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-css&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute-selector z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-entity z-css&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-css&quot;&gt;open&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-attribute-selector z-css&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-css&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-css&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-entity z-css&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;animation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-css&quot;&gt;1&lt;span class=&quot;z-keyword z-other z-unit z-css&quot;&gt;s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;linear&lt;&#x2F;span&gt; details-appear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;animation-fill-mode&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;backwards&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-comment z-block z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-css&quot;&gt;&#x2F;*&lt;&#x2F;span&gt;needed for the max-height animation to work&lt;span class=&quot;z-punctuation z-definition z-comment z-css&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;overflow&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;hidden&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that the opening part is taken care of.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fading-out-of-existence&quot;&gt;Fading out of Existence&lt;&#x2F;h3&gt;
&lt;p&gt;Because the details element immediately closes and there is no way to animate that we have to put a delay between the actual interaction with the element and the &lt;code&gt;open&lt;&#x2F;code&gt; attribute being removed, so let&#x27;s add some JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;Because we want to hook on the interaction of a sighted user with the summary element, we use an &lt;code&gt;onclick&lt;&#x2F;code&gt; handler here and assume a &lt;code&gt;details_click_handler()&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;The following function simply adds the &lt;code&gt;onclick&lt;&#x2F;code&gt; handler to every summary element on the page (if you only want to target specific elements … I&#x27;ll leave that as an exercise).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript z-code&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;z-source z-ts&quot;&gt;&lt;span class=&quot;z-meta z-function z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-ts&quot;&gt;function&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-definition z-function z-ts&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;initalize_animated_details&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-parameters z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-begin z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-end z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;var&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;details_elements&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;document&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;getElementsByTagName&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;summary&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-loop z-ts&quot;&gt;for&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;i&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;i&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-relational z-ts&quot;&gt;&amp;lt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details_elements&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-ts&quot;&gt;length&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-increment z-ts&quot;&gt;++&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;details_elements&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-array z-literal z-ts&quot;&gt;&lt;span class=&quot;z-meta z-brace z-square z-ts&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-square z-ts&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;onclick&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;details_click_handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To run this initalizer you can use any method you like, but since in my experience &lt;code&gt;document.onload&lt;&#x2F;code&gt; is a bit wonky when testing inline code on small documents here is a way to defer it until the first interaction.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript z-code&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;z-source z-ts&quot;&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;document&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;onclick&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-arrow z-ts&quot;&gt; &lt;span class=&quot;z-meta z-parameters z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-begin z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-end z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-arrow z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-arrow z-ts&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-comment z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;*&lt;&#x2F;span&gt; we only need it once &lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;document&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;onclick&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-undefined z-ts&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;initalize_animated_details&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-comment z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;*&lt;&#x2F;span&gt; if the event was for a summary tag run the handler &lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;tagName&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-comparison z-ts&quot;&gt;===&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;SUMMARY&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;details_click_handler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-comment z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;*&lt;&#x2F;span&gt; Otherwise let the browser handle the click &lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-true z-ts&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With that set up we can now focus on the click handler, which has to do two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;add a style-class that will trigger the animation&lt;&#x2F;li&gt;
&lt;li&gt;set a timer to actually close the element after the animation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A bare minimum implementation of the &lt;code&gt;details_click_handler&lt;&#x2F;code&gt;&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript z-code&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;z-source z-ts&quot;&gt;&lt;span class=&quot;z-meta z-function z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-ts&quot;&gt;function&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-definition z-function z-ts&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;details_click_handler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-parameters z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-begin z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-end z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;parentElement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-comment z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;*&lt;&#x2F;span&gt; When opening, let the browser do its job &lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-ts&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;open&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-true z-ts&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;add&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;closing&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-support z-function z-ts&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-expression z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-ts&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-parameters z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-begin z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-end z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;open&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-false z-ts&quot;&gt;false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;remove&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;closing&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;500&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-false z-ts&quot;&gt;false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	&lt;figcaption&gt;Fun fact: if you try that one out right now you&#x27;ll get the experience of someone who is unable to see the closing animation.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;With the bare minimum of JavaScript added let&#x27;s add some CSS to get a proof of concept animated details element. This is basically the reversed opening from the current height, defaulting to &lt;code&gt;100vh&lt;&#x2F;code&gt; to the height of the summary element defaulting to &lt;code&gt;1em&lt;&#x2F;code&gt; again.&lt;&#x2F;p&gt;
&lt;p&gt;Again we&#x27;ll worry about actually setting those variables to something useful later.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;css&quot; class=&quot;language-css z-code&quot;&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;z-source z-css&quot;&gt;&lt;span class=&quot;z-meta z-at-rule z-keyframe z-css&quot;&gt;&lt;span class=&quot;z-keyword z-control z-at-rule z-keyframe z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-keyword z-css&quot;&gt;@&lt;&#x2F;span&gt;keyframes&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-animation-name z-css&quot;&gt;details-disappear&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-keyframe-selector z-css&quot;&gt;from&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-current-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; 100vh&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-keyframe-selector z-css&quot;&gt;to&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-summary-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; 1em&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-selector z-css&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-css&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-class z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-entity z-css&quot;&gt;.&lt;&#x2F;span&gt;closing&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;animation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-close-animation-length&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; .5s&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;linear&lt;&#x2F;span&gt; details-disappear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;overflow&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;hidden&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-summary-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; 1em&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now you should have a not pretty but animated closing details element.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;improving-the-animation&quot;&gt;Improving the Animation&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have a proof of concept, lets make it usable.&lt;&#x2F;p&gt;
&lt;p&gt;The issues we currently have are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Handle an interaction while the animation is playing correctly.&lt;&#x2F;li&gt;
&lt;li&gt;Accessibility impact is larger than it could be.&lt;&#x2F;li&gt;
&lt;li&gt;The heights used are still placeholders.&lt;&#x2F;li&gt;
&lt;li&gt;The animation speeds don&#x27;t match up.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;handling-an-interaction-while-the-animation-is-playing&quot;&gt;Handling an interaction while the animation is playing&lt;&#x2F;h3&gt;
&lt;p&gt;Currently when one interacts while the animation is playing the behaviours is that the element keeps closing, which is not great.&lt;&#x2F;p&gt;
&lt;p&gt;To fix it we have to add some additional checks to our handler function to test if the &lt;code&gt;closing&lt;&#x2F;code&gt; class is present on an open element. If that&#x27;s the case we just remove the class gain instead of launching a new timeout and on the timeout part we just discard the timeout if the closing class isn&#x27;t present.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;lowering-the-impact-on-accessibility&quot;&gt;Lowering the impact on accessibility&lt;&#x2F;h3&gt;
&lt;p&gt;There are two problems I&#x27;m trying to solve with this one:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When only relying on something that uses the accessibility interface of the browser (i.e. screenreader only) there is an inexplicable delay between interaction and the element being announced as collapsed, very confusing and&#x2F;or annoying.&lt;&#x2F;li&gt;
&lt;li&gt;When navigating by keyboard, no matter what the output device is, the content inside the details element can still be focused while the animation is playing, again potentially confusing and&#x2F;or annoying.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of the alone would be enough of a reason (at least for me) to not animate the closing of the element on keyboard interaction. (Fixing this is probably possible but at that point we are writing a full blown expander in JavaScript.)&lt;&#x2F;p&gt;
&lt;p&gt;Based on this we make a simple assumption: If you are not using a pointing device, you are probably not in the group that greatly benefits from an animation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Of course the &amp;quot;uses a pointer&amp;quot; is a heuristic that will be sometimes be wrong.&lt;&#x2F;p&gt;
&lt;p&gt;To achieve this one can test for the &lt;code&gt;offsetX&lt;&#x2F;code&gt; and &lt;code&gt;offsetY&lt;&#x2F;code&gt; values of the event to find out weather the event came from a pointer or not, both are 0 they probably didn&#x27;t come from a pointer and we tell the browser to do what it would do without us interfering.&lt;&#x2F;p&gt;
&lt;p&gt;To stop the opening animation from playing when we won&#x27;t deliver a corresponding closing animation we add an &lt;code&gt;animated&lt;&#x2F;code&gt; class as an additional requirement in our two animation CSS rules.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The offset check to only play animations for events from pointer devices&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript z-code&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;z-source z-ts&quot;&gt;&lt;span class=&quot;z-meta z-function z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-ts&quot;&gt;function&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-definition z-function z-ts&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;details_click_handler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-parameters z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-begin z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-end z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;parentElement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;offsetX&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-comparison z-ts&quot;&gt;==&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;0&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-ts&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;offsetY&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-comparison z-ts&quot;&gt;==&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;remove&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;animated&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-true z-ts&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;add&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;animated&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;	&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; … Rest of the function goes here …&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;This way we can make PR and our bosses happy while mostly staying out of the way of people who appreciate clean websites more than fancy animations.&lt;&#x2F;p&gt;
&lt;p&gt;Note that after this the animation won&#x27;t play if the JavaScript isn&#x27;t running (The element will just snap open and closed like at the start).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deglitching-the-animation&quot;&gt;Deglitching the animation&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have taken care of accessibility issues, we can put the CSS variables introduced earlier to use to make the animation responsive to the current state to avoid some glitches.&lt;&#x2F;p&gt;
&lt;p&gt;This means populating the &lt;code&gt;--details-summary-height&lt;&#x2F;code&gt; and &lt;code&gt;--details-current-height&lt;&#x2F;code&gt; properties directly on the details elements.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;This code is best placed after the offset check so that the properties are already set when opening and closing&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript z-code&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;z-source z-ts&quot;&gt;&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;summary_height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;getBoundingClientRect&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;current_height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;getBoundingClientRect&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;--details-summary-height&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;summary_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;px&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;--details-current-height&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;px&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;getting-the-timings-to-match&quot;&gt;Getting the timings to match&lt;&#x2F;h3&gt;
&lt;p&gt;After that is fixed the last open issue is, that the speed of the animation opening and the speed of the animation closing don&#x27;t match.&lt;&#x2F;p&gt;
&lt;p&gt;With the opening speed currently being one screen height minus summary per second it is pretty easy to get the closing animation to match that as when the accordion is open we can just ask the browser how tall the details and summary elements are, how tall the screen is and calculate the animation length from that. Since we already know the heights of both relevant elements we&#x27;ll use the variables from earlier.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;This code calculating and setting the animation length should be put right before we add the &lt;code&gt;closing&lt;&#x2F;code&gt; CSS class&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript z-code&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;z-source z-ts&quot;&gt;&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;close_animation_duration&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;summary_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;window&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;innerHeight&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;--details-close-animation-length&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;close_animation_duration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;s&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;To make the actual state match the timeout previously set to &lt;code&gt;500&lt;&#x2F;code&gt; should now be &lt;code&gt;close_animation_duration*1000+10&lt;&#x2F;code&gt; the extra 10ms is to allow the browser a bit of breathing room so that we don&#x27;t cut the animation off before it has finished.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-final-result&quot;&gt;The final result&lt;&#x2F;h3&gt;
&lt;p&gt;The resulting &lt;code&gt;details_click_handler&lt;&#x2F;code&gt; function now should look like the following.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; class=&quot;language-javascript z-code&quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;z-source z-ts&quot;&gt;&lt;span class=&quot;z-meta z-function z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-ts&quot;&gt;function&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-definition z-function z-ts&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;details_click_handler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-parameters z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-begin z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-end z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;parentElement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;	&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; if called for a non-pointer event don&amp;#39;t play animations&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;offsetX&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-comparison z-ts&quot;&gt;==&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;0&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-ts&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;offsetY&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-comparison z-ts&quot;&gt;==&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;remove&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;animated&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-true z-ts&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;add&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;animated&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;	&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; tell the animations where to start and where to end&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;summary_height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;getBoundingClientRect&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;current_height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;getBoundingClientRect&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;--details-summary-height&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;summary_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;px&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;--details-current-height&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;px&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;	&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; log the event for debugging&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;	&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; console.log(&amp;quot;click&amp;quot;, event);&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;	&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; let the browser handle the opening&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-ts&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;open&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-true z-ts&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;contains&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;closing&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;		&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; abort a closing animatiton when interrupted&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;remove&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;closing&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;else&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;		&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; calculate close animation length&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-var z-expr z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-ts&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-var-single-variable z-expr z-ts&quot;&gt;&lt;span class=&quot;z-meta z-definition z-variable z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;close_animation_duration&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;current_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;summary_height&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-dom z-ts&quot;&gt;window&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;innerHeight&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-variable z-property z-dom z-ts&quot;&gt;style&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-ts&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;--details-close-animation-length&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;close_animation_duration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;s&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;		&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; trigger closing animation and correponding timeout&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;add&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;closing&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-support z-function z-ts&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-expression z-ts&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-ts&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-parameters z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-begin z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters z-end z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;			&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; discard timeout if someone interrupted the animation&lt;&#x2F;span&gt;
			&lt;span class=&quot;z-keyword z-control z-conditional z-ts&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;contains&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;closing&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;{&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-whitespace z-comment z-leading z-ts&quot;&gt;				&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-ts&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-ts&quot;&gt; properly close the element and clean up the styleclass&lt;&#x2F;span&gt;
				&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-property z-ts&quot;&gt;open&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-ts&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-false z-ts&quot;&gt;false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
				&lt;span class=&quot;z-meta z-function-call z-ts&quot;&gt;&lt;span class=&quot;z-variable z-other z-object z-ts&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-object z-property z-ts&quot;&gt;classList&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-ts&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-dom z-ts&quot;&gt;remove&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-ts&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;closing&lt;span class=&quot;z-punctuation z-definition z-string z-end z-ts&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
			&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-comma z-ts&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-ts&quot;&gt;close_animation_duration&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-ts&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-decimal z-ts&quot;&gt;10&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-brace z-round z-ts&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-control z-flow z-ts&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-boolean z-false z-ts&quot;&gt;false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-statement z-ts&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-definition z-block z-ts&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And our final CSS:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;css&quot; class=&quot;language-css z-code&quot;&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;z-source z-css&quot;&gt;&lt;span class=&quot;z-meta z-at-rule z-keyframe z-css&quot;&gt;&lt;span class=&quot;z-keyword z-control z-at-rule z-keyframe z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-keyword z-css&quot;&gt;@&lt;&#x2F;span&gt;keyframes&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-animation-name z-css&quot;&gt;details-appear&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-keyframe-selector z-css&quot;&gt;from&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-current-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; 1em&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-keyframe-selector z-css&quot;&gt;to&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-css&quot;&gt;100&lt;span class=&quot;z-keyword z-other z-unit z-css&quot;&gt;vh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-at-rule z-keyframe z-css&quot;&gt;&lt;span class=&quot;z-keyword z-control z-at-rule z-keyframe z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-keyword z-css&quot;&gt;@&lt;&#x2F;span&gt;keyframes&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-other z-animation-name z-css&quot;&gt;details-disappear&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-keyframe-selector z-css&quot;&gt;from&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-current-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; 100vh&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-keyword z-keyframe-selector z-css&quot;&gt;to&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
		&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-summary-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; 1em&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;	&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-selector z-css&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-css&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-class z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-entity z-css&quot;&gt;.&lt;&#x2F;span&gt;animated&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute-selector z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-entity z-css&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-css&quot;&gt;open&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-attribute-selector z-css&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-css&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-css&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-entity z-css&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;animation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-css&quot;&gt;1&lt;span class=&quot;z-keyword z-other z-unit z-css&quot;&gt;s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;linear&lt;&#x2F;span&gt; details-appear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;animation-fill-mode&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;backwards&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;overflow&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;hidden&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-selector z-css&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-css&quot;&gt;details&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-class z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-entity z-css&quot;&gt;.&lt;&#x2F;span&gt;animated&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-other z-attribute-name z-class z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-entity z-css&quot;&gt;.&lt;&#x2F;span&gt;closing&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-list z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;animation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-close-animation-length&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; .5s&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;linear&lt;&#x2F;span&gt; details-disappear&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;overflow&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-support z-constant z-property-value z-css&quot;&gt;hidden&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-property-name z-css&quot;&gt;&lt;span class=&quot;z-support z-type z-property-name z-css&quot;&gt;max-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-css&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property-value z-css&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-css&quot;&gt;&lt;span class=&quot;z-support z-function z-var z-css&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-begin z-css&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-custom-property z-css&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-custom-property z-name z-css&quot;&gt;details-summary-height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-sequence z-css&quot;&gt;,&lt;&#x2F;span&gt; 1em&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-css&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-group z-end z-css&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rule z-css&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-property-list z-css&quot;&gt;}&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;updates&quot;&gt;Updates&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2023-03-25&quot;&gt;2023-03-25&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Added note about accordions usually having a grouping functionality.&lt;&#x2F;li&gt;
&lt;li&gt;Added link to a [great article by Scott O&#x27;Hara explaining the problems around how details element is implemented in browsers].&lt;&#x2F;li&gt;
&lt;li&gt;Added explanation why I&#x27;m disabling the animation for non-pointer inputs here.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>IP-Address Ranges</title>
        <published>2023-03-08T00:00:00+00:00</published>
        <updated>2023-03-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/ip-ranges/"/>
        <id>https://slatecave.net/notebook/ip-ranges/</id>
        
        <summary type="text">Overview of reserved IP-Address ranges for IPv4 and IPv6</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/ip-ranges/">&lt;h2 id=&quot;in-general&quot;&gt;In General&lt;&#x2F;h2&gt;
&lt;p&gt;While IPv4 and IPv6 have different terminology, they share some concepts. Scope and Delivery schemes.&lt;&#x2F;p&gt;
&lt;p&gt;Both IP-Versions share the following delivery schemes:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		Unicast 
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		one to one, the default
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Multicast
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		one to many, v6 had a huge Update there
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Broadcast
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		usually inside the scope of a single network
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Scope is a bit version dependant, usually you have:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		Global
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These addresses are globally routable and assigned by the &lt;a href=&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;&quot;&gt;IANA&lt;&#x2F;a&gt; and the local Internet registries.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Private
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These addresses are reserved for private use and you are free to use the in your own network, just be aware, that you can&#x27;t use them on the internet.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Loopback
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These addresses always route back to the sending machine, usually &lt;code&gt;localhost&lt;&#x2F;code&gt; resolves to them.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Link-Local
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These adresses are automatically assigned so that host on the same network can talk to each other.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These addresses are reserved for documentation purposes.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Other reserved
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		These adresses are reserved and have some conditions on when you are supposed to use them.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;ipv6-adress-ranges&quot;&gt;IPv6 Adress Ranges&lt;&#x2F;h2&gt;
&lt;p&gt;By default IPv6 adresses are unassigned global unicast adresses and therefore reserved for future use. The IANA has a complete list with the &lt;a href=&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;assignments&#x2F;iana-ipv6-special-registry&#x2F;iana-ipv6-special-registry.xhtml&quot;&gt;IANA IPv6 Special-Purpose Address Registry&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		Global Unicast (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.5.4&quot;&gt;RFC 4291, Section 2.5.4&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;2000::&#x2F;3&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Unique Local (Private) (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4193&quot;&gt;RFC 4193&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;fd00::&#x2F;8&lt;&#x2F;code&gt; (with Random Global ID)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;fc00::&#x2F;8&lt;&#x2F;code&gt; (reserved for future use)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Loopback (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.5.3&quot;&gt;RFC 4291, Section 2.5.3&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;::1&#x2F;128&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Link-Local (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.5.6&quot;&gt;RFC 4291, Section 2.5.6&lt;&#x2F;a&gt;
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;fe80::&#x2F;10&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc3849.html&quot;&gt;RFC 3849&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;2001:db8::&#x2F;32&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Multicast (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.7&quot;&gt;RFC 4291, Section 2.7&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;ff00::&#x2F;8&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		IETF Protocol Assignments (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc6890.html#section-2.1&quot;&gt;RFC 6890&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;2001:0000::&#x2F;23&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Site-Local IPv6 Unicast Addresses (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4291.html#section-2.5.7&quot;&gt;RFC 4291, Section 2.5.7&lt;&#x2F;a&gt;) have been deprecated in favour of Unique Local Addresses (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc4193&quot;&gt;RFC 4193&lt;&#x2F;a&gt;) which are pretty similar, but better defined.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ipv4-address-ranges&quot;&gt;IPv4 Address Ranges&lt;&#x2F;h2&gt;
&lt;p&gt;By default IPv4 adresses are adresses that fall in the global unicast category. The IANA has a complete List with the &lt;a href=&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;assignments&#x2F;iana-ipv4-special-registry&#x2F;iana-ipv4-special-registry.xhtml#iana-ipv4-special-registry-1&quot;&gt;IANA IPv4 Special-Purpose Address Registry&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		Private (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc1918.html&quot;&gt;RFC 1918&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;192.168.0.0&#x2F;16&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;172.16.0.0&#x2F;12&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;10.0.0.0&#x2F;8&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Loopback (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc1122.html#page-31&quot;&gt;RFC 1122, Section 3.2.1.3&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;127.0.0.0&#x2F;8&lt;&#x2F;code&gt; Note that this is an entire network.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;127.0.0.1&lt;&#x2F;code&gt; is usually used.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Link-Local (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc3927.html&quot;&gt;RFC 2927&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;169.254.0.0&#x2F;16&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Documentation (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc5737.html#section-3&quot;&gt;RFC 5737&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;192.0.2.0&#x2F;24&lt;&#x2F;code&gt; (TEST-NET-1)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;198.51.100.0&#x2F;24&lt;&#x2F;code&gt; (TEST-NET-2)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;203.0.113.0&#x2F;24&lt;&#x2F;code&gt; (TEST-NET-3)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Shared (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc6598.html&quot;&gt;RFC 6598&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;100.64.0.0&#x2F;10&lt;&#x2F;code&gt; &lt;br&gt;&lt;q&gt;used as Shared Address Space to accommodate the needs of Carrier-Grade NAT (CGN) devices.&lt;&#x2F;q&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Benchmarking (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc2544.html#appendix-C.2.2.1&quot;&gt;RFC 2544&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;198.18.0.0&#x2F;15&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		Multicast
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;240.0.0.0&#x2F;4&lt;&#x2F;code&gt; Multicast &amp;quot;Namespace&amp;quot; (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc1112.html#section-4&quot;&gt;RFC 1112&lt;&#x2F;a&gt;)
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;192.52.193.0&#x2F;24&lt;&#x2F;code&gt; Automatic Multicast Tunneling (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc7450.html&quot;&gt;RFC 7450&lt;&#x2F;a&gt;)
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		IETF Protocol Assignments (&lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc6890.html#section-2.1&quot;&gt;RFC 6890&lt;&#x2F;a&gt;)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		&lt;code&gt;192.0.0.0&#x2F;24&lt;&#x2F;code&gt;
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;broadcast&quot;&gt;Broadcast&lt;&#x2F;h3&gt;
&lt;p&gt;Broadcast addresses with IPv4 work by setting the host-part of the address to all ones (in the binary representation) they send to all hosts in the local network.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; In &lt;a href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc919.html&quot;&gt;RFC 919&lt;&#x2F;a&gt; &lt;code&gt;255.255.255.255&lt;&#x2F;code&gt; is defined as &lt;q&gt;broadcast to all of its immediate neighbors&lt;&#x2F;q&gt;.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>echoip.slatecave.net</title>
        <published>2023-02-26T00:00:00+00:00</published>
        <updated>2023-02-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/echoip-slatecave/"/>
        <id>https://slatecave.net/creations/echoip-slatecave/</id>
        
        <summary type="text">A web-service for looking up IPs and domain names</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/echoip-slatecave/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;echoip.slatecave.net&quot;&gt;
		Visit echoip.slatecave.net
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;This service was born out of a combination of wanting to learn rust, not being very happy with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mpolden&#x2F;echoip&quot;&gt;the echoip service by mpolden&lt;&#x2F;a&gt; which had some features missing that I wanted in my echoip service.&lt;&#x2F;p&gt;
&lt;p&gt;So what echoip-slatecave do?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Look up IP-Addresses in a mmdb database and return the resulting geoinformation.&lt;&#x2F;li&gt;
&lt;li&gt;Reverse DNS lookup&lt;&#x2F;li&gt;
&lt;li&gt;DNS forward lookup for the most common record types, all on one page&lt;&#x2F;li&gt;
&lt;li&gt;Has a text view that is made for commandline use&lt;&#x2F;li&gt;
&lt;li&gt;Links to other services that may be useful in the HTML version&lt;&#x2F;li&gt;
&lt;li&gt;Has a builtin rate-limiter that makes easier to protect a small self hosted instance&lt;&#x2F;li&gt;
&lt;li&gt;Interlinked IP-Address and domain lookup to make navigating easier&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With that feature set it should be a useful tool where one can enter any kind of address and navigate straight to the needed information or start exploring (which works pretty well, when the IP and domain lookup are interlinked).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why?&lt;&#x2F;h2&gt;
&lt;p&gt;But slatian, why? And why include a DNS lookup? I can do this on the commandline!&lt;&#x2F;p&gt;
&lt;p&gt;Besides the fact that exploring is easier with a web interface that does multiple lookup with which is one of the reasons I built it …&lt;&#x2F;p&gt;
&lt;p&gt;Yes YOU can use the commandline and make sense of it, I can too. But not everyone has the skill or system to use a capable commandline, also I don&#x27;t want to link to some random non-free wannabe monopoly service (&lt;i&gt;gat&lt;&#x2F;i&gt; it?) because if someone didn&#x27;t have a tool they&#x27;ll continue using that.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Serial Port Troubleshooting on Linux</title>
        <published>2023-02-14T00:00:00+00:00</published>
        <updated>2023-02-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/serial-not-working/"/>
        <id>https://slatecave.net/notebook/serial-not-working/</id>
        
        <summary type="text">How to find out why your serial Port isn&#x27;t working</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/serial-not-working/">&lt;p&gt;A serial port (i.e. for programming a microcontroller) can have multiple resons why it is not working. This guide is based on my experince and will try to cover most cases, FAQ style.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hints&quot;&gt;Hints&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;It is always a good idea to try to look up the problem in your distributions wiki&#x2F;documentation first!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can manually connect to a serial port using &lt;code&gt;screen &#x2F;dev&#x2F;tty&amp;lt;name&amp;gt;&lt;&#x2F;code&gt;.
Quit with &lt;kbd&gt;Ctrl+a&lt;&#x2F;kbd&gt; and pressing &lt;kbd&gt;k&lt;&#x2F;kbd&gt; after that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;is-your-serial-device-showing-up&quot;&gt;Is your Serial device showing up?&lt;&#x2F;h2&gt;
&lt;p&gt;Use &lt;code&gt;ls &#x2F;dev&#x2F;tty*&lt;&#x2F;code&gt; to get a list of terminal devices on your Linux machine. If you find a &lt;code&gt;ttyUSBn&lt;&#x2F;code&gt; or a &lt;code&gt;ttyACMn&lt;&#x2F;code&gt; (where &lt;code&gt;n&lt;&#x2F;code&gt; is a number) that appears when you plug your device in and disappears when unplugging (yes that&#x27;s the easiest way) that is your device with hardware and drivers working, skip this section.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Builtin serial ports are sometimes also called &lt;code&gt;&#x2F;dev&#x2F;Sn&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;is-the-hardware-recognized&quot;&gt;Is the Hardware recognized?&lt;&#x2F;h3&gt;
&lt;p&gt;Try running &lt;code&gt;lsusb&lt;&#x2F;code&gt;, it will give you a list of connected USB devices, if your device is not among them you either have a bad cable, a bad microcontroller or (for some micros) the controller is not in programming mode.&lt;&#x2F;p&gt;
&lt;p&gt;If the device shows up in &lt;code&gt;lsusb&lt;&#x2F;code&gt; but there is no serial port, your harware is working but there is a driver problem. (or the hardware doesn&#x27;t provide a serial port.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;would-a-reboot-fix-the-problem&quot;&gt;Would a reboot fix the problem?&lt;&#x2F;h3&gt;
&lt;p&gt;This only the case if there has been a system update since the last reboot.&lt;&#x2F;p&gt;
&lt;p&gt;(Depending on your definition of a shortcut: just reboot)&lt;&#x2F;p&gt;
&lt;p&gt;If you are not using a very uncommon setup check if &lt;code&gt;&#x2F;lib&#x2F;modules&lt;&#x2F;code&gt; (the directory kernel modules (drivers) are in) contains a directory called the same as the output of &lt;code&gt;uname -r&lt;&#x2F;code&gt; (the version of the currently running kernel).&lt;&#x2F;p&gt;
&lt;p&gt;If that is not the case your package manager has cleaned up the old modules during an update and you have not rebooted yet (still running the old kernel). The solution in this case is a reboot, then the hardware management will be able to find the modules the kernel needs because the running version matches the version of the modules in the filesystem.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;missing-package-or-driver-error&quot;&gt;Missing Package or Driver Error&lt;&#x2F;h3&gt;
&lt;p&gt;If a reboot did not or would not fix your problem try searching for a driver package (usually the package name contains &lt;code&gt;ftdi&lt;&#x2F;code&gt; for USB to serial adaptors).&lt;&#x2F;p&gt;
&lt;p&gt;If you find the neccessary packe already installed, running &lt;code&gt;dmesg -w&lt;&#x2F;code&gt; as root and then plugging in the device is also worth a look at this point. &lt;code&gt;dmesg&lt;&#x2F;code&gt; prints logs from your kernel, including potential information and errors from relevant drivers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;do-you-have-permission-for-accessing-serial-ports&quot;&gt;Do you have permission for accessing serial ports?&lt;&#x2F;h2&gt;
&lt;p&gt;Run &lt;code&gt;ls -l &#x2F;dev&#x2F;tty*&lt;&#x2F;code&gt;, this will tell you who owns the device files, file permissions apply like with regular files.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;An example output line, the file belongs to the &lt;code&gt;root&lt;&#x2F;code&gt; user and the &lt;code&gt;dialout&lt;&#x2F;code&gt; group, both are allowed to read and write.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;crw-rw---- 1 root dialout 4, 67  5. Sep 12:25 &#x2F;dev&#x2F;ttyS0
#^  ^ The 2 rw here indicate that owner and group may read from and write to this device.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;By default on many distributions the hardware management comes preconfigured in a way that only permits users in the &lt;code&gt;dialout&lt;&#x2F;code&gt; group to use serial ports. Check if are a member of that group using the &lt;code&gt;groups&lt;&#x2F;code&gt; command.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; the &lt;code&gt;dialout&lt;&#x2F;code&gt; group may have a different name on some distributions if the &lt;code&gt;ls -l&lt;&#x2F;code&gt; didn&#x27;t indicate that the serial port belongs to the &lt;code&gt;dialout&lt;&#x2F;code&gt; group this is the case for you. (look up serial ports in the distributions wiki).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on Arch derived Distributions:&lt;&#x2F;b&gt; On Archlinux and derived distributions the relevant group is called &lt;code&gt;uucp&lt;&#x2F;code&gt; instead of &lt;code&gt;dialout&lt;&#x2F;code&gt;. (UUCP stands for &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;UUCP&quot;&gt;Unix-to-Unix Copy&lt;&#x2F;a&gt; which was common to be used over a serial link.)&lt;&#x2F;p&gt;
&lt;p&gt;You can give a user permission by running &lt;code&gt;sudo usermod -a -G dialout &amp;lt;username&amp;gt;&lt;&#x2F;code&gt; and logging out and back in after that.&lt;&#x2F;p&gt;
&lt;p&gt;In case your system looks completely different you may want to investigate &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;udev&quot;&gt;&lt;code&gt;udev&lt;&#x2F;code&gt; and udev rules&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;is-something-else-using-the-port&quot;&gt;Is something else using the port?&lt;&#x2F;h2&gt;
&lt;p&gt;Since a program doesn&#x27;t really know what is on the other side of a serial port some programs simply connect and try to find out. This may interfere with whatever you are trying to do.&lt;&#x2F;p&gt;
&lt;p&gt;One of these programs is &lt;a href=&quot;https:&#x2F;&#x2F;packages.debian.org&#x2F;bullseye&#x2F;brltty&quot;&gt;brltty&lt;&#x2F;a&gt; which is preconfigured by most big distributions. It tries very hard to make sure plugging a braille display into an USB port will make it immedeately available. (which is very good, your little Arduino isn&#x27;t nearly as important!) Unfortunately it has to open every serial device taht connects to the system to find out wheter it is a braille display or not.&lt;&#x2F;p&gt;
&lt;p&gt;To fix that behaviour either configure the &amp;quot;offending&amp;quot; program or stop (and disable) the service it belongs to if you don&#x27;t need it.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example of how to disable and stop the &lt;code&gt;brltty&lt;&#x2F;code&gt; service on most systemd based distributions. &lt;br&gt;&lt;strong&gt;Do not do this if you rely on a working braille display!&lt;&#x2F;strong&gt;&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; systemctl disable&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;now&lt;&#x2F;span&gt; brltty&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;did-that-fix-it&quot;&gt;Did that fix it?&lt;&#x2F;h2&gt;
&lt;p&gt;If this guide helped you please consider sharing it.&lt;&#x2F;p&gt;
&lt;p&gt;In case you found a mistake  or ended up in a dead end, don&#x27;t hesitate to &lt;a href=&quot;&#x2F;about&#x2F;me#contact&quot;&gt;contact me&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>arduino-cli</title>
        <published>2023-02-13T00:00:00+00:00</published>
        <updated>2023-02-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/arduino-cli/"/>
        <id>https://slatecave.net/notebook/arduino-cli/</id>
        
        <summary type="text">Some arduino-cli common patterns and tricks</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/arduino-cli/">&lt;p&gt;While &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; is an awesome tool, unfortunately most guides skip right past it and assume you use the Arduino IDE, this page is a collection of common patterns for &lt;code&gt;arduino-cli&lt;&#x2F;code&gt;. It assumes that you know a few things about how Arduino works.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;basics&quot;&gt;Basics&lt;&#x2F;h2&gt;
&lt;p&gt;To create a sketch &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; assumes that your main &lt;code&gt;.ino&lt;&#x2F;code&gt; file has the same name as the directory containing it.&lt;&#x2F;p&gt;
&lt;p&gt;To build and upload use &lt;code&gt;arduino-cli compile --fqbn &amp;lt;fqbn&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;arduino-cli upload -p &amp;lt;serial-device&amp;gt; --fqbn &amp;lt;fqbn&amp;gt;&lt;&#x2F;code&gt; in your code directory.&lt;&#x2F;p&gt;
&lt;p&gt;!note: &lt;b&gt;Hint:&lt;&#x2F;b&gt; On Linux your serial devices are called &lt;code&gt;&#x2F;dev&#x2F;ttyUSBn&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;dev&#x2F;ttyACMn&lt;&#x2F;code&gt; where &lt;code&gt;n&lt;&#x2F;code&gt; is a number.&lt;br&gt; &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;serial-not-working&#x2F;&quot;&gt;Make sure you have the permissions to access them and nothing else is trying to use those ports.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;!note: &lt;b&gt;You&#x27;ve got an unhelpful error message?&lt;&#x2F;b&gt; Try adding the &lt;code&gt;--log&lt;&#x2F;code&gt; flag, it will cause &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; to print out whatever it is currently doing.&lt;br&gt; This way one can get some context for the error.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fqbn-fully-qualified-board-name&quot;&gt;FQBN? Fully Qualified Board Name!&lt;&#x2F;h2&gt;
&lt;p&gt;The Fully qualified Board name consists of what Arduino calls the &lt;i&gt;Platform ID&lt;&#x2F;i&gt; and a name for the board. You can find it out either by using &lt;code&gt;arduino-cli board search &amp;lt;searchterm&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;arduino-cli board listall&lt;&#x2F;code&gt; or by appending the official board name in lowercase to the Platform ID.&lt;&#x2F;p&gt;
&lt;p&gt;Examples include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arduino:avr:uno&lt;&#x2F;code&gt; (Arduino Uno or another board with an ATMega328P running at 16MHz)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;esp8266:esp8266:generic&lt;&#x2F;code&gt; (A generic esp8266 Module)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;esp32:esp32:esp32&lt;&#x2F;code&gt; (A generic esp32 module)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Please note that the esp boards are not available by default.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compile-and-upload-a-sketch-using-arduino-cli&quot;&gt;Compile and Upload a Sketch using arduino-cli&lt;&#x2F;h2&gt;
&lt;p&gt;I usually create a &lt;code&gt;compile-and-upload.sh&lt;&#x2F;code&gt; script to make development and testing easier.&lt;&#x2F;p&gt;
&lt;figure&gt;
	
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;arduino-cli&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; compile&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;fqbn&lt;&#x2F;span&gt; arduino:avr:uno&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-and z-shell&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;arduino-cli&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; upload&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;fqbn&lt;&#x2F;span&gt; arduino:avr:uno&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;p&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;:-&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&#x2F;dev&#x2F;ttyUSB0&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	&lt;figcaption&gt;A simple compile and upload script with a fixed board and an optional argument for the serial port that defaults to &lt;code&gt;&#x2F;dev&#x2F;ttyUSB0&lt;&#x2F;code&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; When given the &lt;code&gt;-u&lt;&#x2F;code&gt; flag and the options needed for uploading &lt;code&gt;arduino-cli compile --fqbn arduino:avr:uno -u -p &amp;quot;${1:-&#x2F;dev&#x2F;ttyUSB0}&amp;quot;&lt;&#x2F;code&gt; behaves like the above example script.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on shell syntax:&lt;&#x2F;b&gt; The &lt;code&gt;&amp;quot;${1:-&#x2F;dev&#x2F;ttyUSB0}&amp;quot;&lt;&#x2F;code&gt; is read by your shell as replace this with the first argument given to the script or with &lt;code&gt;&#x2F;dev&#x2F;ttyUSB0&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-more-boards&quot;&gt;Adding more Boards&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; is not just a wrapper for a compiler and whatever you prefer for uploading … it also is a package manager. If you want more boards (or libraries) you can search for and install them.&lt;&#x2F;p&gt;
&lt;p&gt;But if you want to write code for a non-Arduino branded board (i.e. an ESP8266 or ESP32) those packages are not in the Arduino package repositories.&lt;&#x2F;p&gt;
&lt;p&gt;Adding an extra repository can be done by letting &lt;code&gt;arduino-cli&lt;&#x2F;code&gt; do the work …&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;arduino-cli&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; config add board_manager.additional_urls &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;url:&#x2F;&#x2F;to&#x2F;index.json&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;… or by manually editing &lt;code&gt;~&#x2F;.arduino15&#x2F;arduino-cli.yaml&lt;&#x2F;code&gt; and adding the URL pinting to the respositories &lt;code&gt;*_index.json&lt;&#x2F;code&gt; file there.&lt;&#x2F;p&gt;
&lt;figure&gt;
	
	&lt;pre data-lang=&quot;yaml&quot; class=&quot;language-yaml z-code&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;board_manager&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-mapping z-yaml&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;additional_urls&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-mapping z-yaml&quot;&gt;:&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-punctuation z-definition z-block z-sequence z-item z-yaml&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;https:&#x2F;&#x2F;arduino.esp8266.com&#x2F;stable&#x2F;package_esp8266com_index.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-punctuation z-definition z-block z-sequence z-item z-yaml&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;https:&#x2F;&#x2F;dl.espressif.com&#x2F;dl&#x2F;package_esp32_index.json&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-punctuation z-definition z-block z-sequence z-item z-yaml&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;espressif&#x2F;arduino-esp32&#x2F;gh-pages&#x2F;package_esp32_dev_index.json&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	&lt;figcaption&gt;An excerpt from the configuration file from my laptop with additional URL to the stable esp8266 and esp32 repositories in addition to the development version of the esp32 repository.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Don&#x27;t forget to &lt;code&gt;arduino-cli update&lt;&#x2F;code&gt; and &lt;code&gt;arduino-cli upgrade&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;flashing-a-bootloader-using-arduino-cli&quot;&gt;Flashing a Bootloader using arduino-cli&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;arduino-cli&lt;&#x2F;code&gt; already knows how to flash a new bootloader with the &lt;code&gt;burn-bootloader&lt;&#x2F;code&gt; subcommand, you have to tell it for which board you want the bootloader using &lt;code&gt;--fqbn&lt;&#x2F;code&gt; and which programmer you are using with &lt;code&gt;-P&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; Programmers usually aren&#x27;t quite cheap, but if you have another Arduino (compatible) with UART (serial) and SPI interface you can use that &lt;a href=&quot;https:&#x2F;&#x2F;docs.arduino.cc&#x2F;built-in-examples&#x2F;arduino-isp&#x2F;ArduinoISP&quot;&gt;Arduino as an &lt;abbr title=&quot;In-circuit Serial Programmer&quot;&gt;ISP&lt;&#x2F;abbr&gt; &lt;&#x2F;a&gt;. (use &lt;code&gt;-P arduinoasisp&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;It may be necessary to provide the port the programmer is on via the &lt;code&gt;-p&lt;&#x2F;code&gt; option. (like with the &lt;code&gt;upload&lt;&#x2F;code&gt; command)&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example using the official AVRISP mkII programmer for AVR chips for an ATMEL 328P in an Arduino Uno like configuration&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;arduino-cli&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; burn-bootloader&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;fqbn&lt;&#x2F;span&gt; arduino:avr:uno&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;P&lt;&#x2F;span&gt; avrispmkii&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;To find out which programmer string you need, go to the &lt;code&gt;ArduinoCore-*&lt;&#x2F;code&gt; repository of your favourite architecture (See &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arduino&quot;&gt;github.com&#x2F;arduino&lt;&#x2F;a&gt; for the official ones) and open the &lt;code&gt;programmers.txt&lt;&#x2F;code&gt; file (i.e. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arduino&#x2F;ArduinoCore-avr&#x2F;blob&#x2F;master&#x2F;programmers.txt&quot;&gt;ArduinoCore-avr&#x2F;programmers.txt&lt;&#x2F;a&gt;), the keys used there are the needed ids for the &lt;code&gt;-P&lt;&#x2F;code&gt; option.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-do-i-need-a-bootloader&quot;&gt;Why do I need a Bootloader?&lt;&#x2F;h3&gt;
&lt;p&gt;To be able to seamlessly upload code with just a USB to serial converter like in the example above your microcontroller needs to have the Arduino bootloader installed, a small piece of software that runs when the controller starts (that&#x27;s the reason you need to hook up the reset line when programming) that can be told to write a new program to the controller instead of starting the already existing one.&lt;&#x2F;p&gt;
&lt;p&gt;The boards you can buy online come with a bootloader preinstalled. But maybe you want to build your own Arduino compatible or released the magic smoke and want to just buy the chip itself without a development-board, that is when you want to flash the bootloader yourself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;do-i-need-a-bootloader-to-use-the-arduino-framwork&quot;&gt;Do I need a Bootloader to use the Arduino Framwork?&lt;&#x2F;h3&gt;
&lt;p&gt;No, you can use the programmer of your choice (just use the &lt;code&gt;-P&lt;&#x2F;code&gt; option with the upload command) to flash whatever yo coded up directly into the controller at the cost of having to use the programmer every time you upload a new program. (Unless that program is a bootloader 😉)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-sketch-local-libraries&quot;&gt;Using sketch-local Libraries&lt;&#x2F;h2&gt;
&lt;p&gt;To use liberies stored anywhere but the default location (i.e. in a &lt;code&gt;libs&lt;&#x2F;code&gt; folder next to your &lt;code&gt;.ino&lt;&#x2F;code&gt; files) the &lt;code&gt;--library&lt;&#x2F;code&gt; and &lt;code&gt;--libraries&lt;&#x2F;code&gt; can be used. Both accept a comma seperated list of filepaths.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;--library&lt;&#x2F;code&gt; points to individual libraries and &lt;code&gt;--libraries&lt;&#x2F;code&gt; points to folders of libraries.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example to compile a sketch with a &lt;code&gt;libs&lt;&#x2F;code&gt; folder&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;arduino-cli&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; compile&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;libraries&lt;&#x2F;span&gt; libs&#x2F;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;fqbn&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;fqbn&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;you-may-also-be-interested-in&quot;&gt;You may also be interested in&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;arduino.github.io&amp;#x2F;arduino-cli&amp;#x2F;latest&amp;#x2F;getting-started&amp;#x2F;&quot;&gt;
		The official arduino-cli documantation
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;notebook&#x2F;serial-not-working&#x2F;&quot;&gt;
		Serial Port Troublehooting on Linux
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Resistor Value Table</title>
        <published>2023-01-14T00:00:00+00:00</published>
        <updated>2023-01-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/resistors/"/>
        <id>https://slatecave.net/notebook/resistors/</id>
        
        <summary type="text">For when those Rings don&#x27;t make sense again.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/resistors/">&lt;h2 id=&quot;how-to-read-resistors&quot;&gt;How To Read Resistors&lt;&#x2F;h2&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Color&lt;&#x2F;th&gt;&lt;th&gt;Value&lt;&#x2F;th&gt;&lt;th&gt;Multiplier&lt;&#x2F;th&gt;&lt;th&gt;Tolerance&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row black&quot;&gt;&lt;td&gt;Black&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1Ω&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row brown&quot;&gt;&lt;td&gt;Brown&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;10Ω&lt;&#x2F;td&gt;&lt;td&gt;± 1%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row red&quot;&gt;&lt;td&gt;Red&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;100Ω&lt;&#x2F;td&gt;&lt;td&gt;± 2%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row orange&quot;&gt;&lt;td&gt;Orange&lt;&#x2F;td&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;1KΩ&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row yellow&quot;&gt;&lt;td&gt;Yellow&lt;&#x2F;td&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;10KΩ&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row green&quot;&gt;&lt;td&gt;Green&lt;&#x2F;td&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;100KΩ&lt;&#x2F;td&gt;&lt;td&gt;± 0.5%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row blue&quot;&gt;&lt;td&gt;Blue&lt;&#x2F;td&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td&gt;1MΩ&lt;&#x2F;td&gt;&lt;td&gt;± 0.25%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row violet&quot;&gt;&lt;td&gt;Violet&lt;&#x2F;td&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td&gt;10MΩ&lt;&#x2F;td&gt;&lt;td&gt;± 0.10%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row gray&quot;&gt;&lt;td&gt;Gray&lt;&#x2F;td&gt;&lt;td&gt;8&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;± 0.05%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row white&quot;&gt;&lt;td&gt;White&lt;&#x2F;td&gt;&lt;td&gt;9&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row gold&quot;&gt;&lt;td&gt;Gold&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;0.1Ω&lt;&#x2F;td&gt;&lt;td&gt;± 5%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr class=&quot;color-row silver&quot;&gt;&lt;td&gt;Silver&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;0.01Ω&lt;&#x2F;td&gt;&lt;td&gt;± 10%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Resistors are usually marked with 4 or 5 colored rings to indicate value and tolerance. To read the code in the right direction you have to know that the order is:&lt;&#x2F;p&gt;
&lt;p&gt;1st Ring, 2nd Ring, 3rd Ring (only when you have a 5-Ring code), Multiplier Ring, a larger gap (sometimes not), Tolerance Ring&lt;&#x2F;p&gt;
&lt;p&gt;To decode, read the 1st to 3rd Ring as if they were decimals and then multiply that number with the Multiplier.&lt;&#x2F;p&gt;
&lt;p&gt;For SMD resistors the pattern apparently repeats itself where the last digit on the package is a Multiplier for the other digits, resulting in a value in ohms. Also the form where there is an &lt;code&gt;R&lt;&#x2F;code&gt; in place of the decimal point is common for smaller values.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>FFTFM</title>
        <published>2022-12-11T00:00:00+00:00</published>
        <updated>2022-12-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/fftfm/"/>
        <id>https://slatecave.net/creations/fftfm/</id>
        
        <summary type="text">A fuzzyfinder script that can search for manpages and RFCs</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/fftfm/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;This script uses &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;junegunn&#x2F;fzf&quot;&gt;&lt;code&gt;fzf&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; hooks to make it possible to search with &amp;quot;live&amp;quot; result preview for manpages or RFCs, it also contains some experimental special syntax logic which is supposed to be used for filtering sections of manuals but that part doesn&#x27;t work very well. It also has issues sorting a particualr manpage to the top when an exact name in typed in (probably due to how &lt;code&gt;apropos&lt;&#x2F;code&gt; works)&lt;&#x2F;p&gt;
&lt;p&gt;Note that for the RFCs to work it assumes they are in the place and structure the archlinux &lt;a href=&quot;https:&#x2F;&#x2F;archlinux.org&#x2F;packages&#x2F;community&#x2F;any&#x2F;rfc&#x2F;&quot;&gt;&lt;code&gt;rfc&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; package places them.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Podcasts</title>
        <published>2022-11-06T00:00:00+00:00</published>
        <updated>2022-11-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/podcasts/"/>
        <id>https://slatecave.net/notebook/podcasts/</id>
        
        <summary type="text">A list of podcasts I listen to and can recommend</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/podcasts/">&lt;p&gt;Note that these are mostly about IT and (I think) well known podcasts.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;english-language-podcasts&quot;&gt;English Language Podcasts&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;foss-and-crafts&quot;&gt;FOSS and Crafts&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;fossandcrafts.org&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;fossandcrafts.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;FOSS and Crafts is an interdisciplinary exploration of collaborative creation. […]  We explore such topics as computer programming, craft production, user freedom and agency, especially informed by the principles of the F(L)OSS (Free, Libre, and Open Source Software) and free culture movements.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;postmarketos-podcast&quot;&gt;postmarketOS Podcast&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;cast.postmarketos.org&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;cast.postmarketos.org&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You just found the corner of the Internet, where postmarketOS team members discuss whatever crazy development details we encounter while cooking up our favorite mobile Linux distribution. Besides news and anecdotes, once in a while we throw in an interview with an exciting guest who brings us their unique view on the wider mobile Linux scene.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;deutschsprachige-podcasts&quot;&gt;Deutschsprachige Podcasts&lt;&#x2F;h2&gt;
&lt;!--LANGUAGE:de--&gt;
&lt;h3 id=&quot;chaosradio&quot;&gt;Chaosradio&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;chaosradio.de&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;chaosradio.de&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chaosradio ist der Podcast des Chaos Computer Club Berlin (CCCB). In dem (bis zu) zweistündigen Format informieren wir seit 1995 über technische und gesellschaftliche Themen. &lt;a href=&quot;https:&#x2F;&#x2F;chaosradio.de&#x2F;about&quot;&gt;Weiterlesen …&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;c-radar&quot;&gt;C-RadaR&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.c-radar.de&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;www.c-radar.de&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Monatliche Radiosendung des Chaos Computer Clubs auf Radio Darmstadt. Jeden 2ten Donnerstag im Monat, 21-23 Uhr. 103,4 MHz UKW &#x2F; DAB+ &#x2F; Stream. Tune In!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;pentaradio&quot;&gt;Pentaradio&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;www.c3d2.de&amp;#x2F;radio.html&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;www.c3d2.de&amp;#x2F;radio.html
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Magazin für Netzkultur und Politik des Chaos Computer Clubs Dresden C3D2.
jeden vierten Dienstag im Monat 21:30 – 23:00 Uhr
&lt;cite&gt;(Beschreibung: &lt;a href=&quot;https:&#x2F;&#x2F;coloradio.org&#x2F;?page_id=5199&quot;&gt;coloRadio&lt;&#x2F;a&gt;)&lt;&#x2F;cite&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Webdirectories</title>
        <published>2022-11-03T00:00:00+00:00</published>
        <updated>2022-11-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/webdirectories/"/>
        <id>https://slatecave.net/notebook/webdirectories/</id>
        
        <summary type="text">A collection of link collections I consider worth sharing</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/webdirectories/">&lt;h2 id=&quot;webrings&quot;&gt;Webrings&lt;&#x2F;h2&gt;
&lt;p&gt;Listed roughly in order of preference, it is &lt;em&gt;NOT&lt;&#x2F;em&gt; a ranking. Prefernce in this case means: In therms of how many sites I personally conider interesting. Webrings I found more recently or just don&#x27;t explore as often will naturally rank lower.&lt;&#x2F;p&gt;
&lt;p&gt;In case you are new to webrings: Webrings are sometimes members of other webrings and a few sites are members of a lot of webrings so the below are just some possible entry points.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fediring&quot;&gt;Fediring&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;fediring.net&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;fediring.net
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This webring is for the personal sites of any member of the fediverse (also known as fedizens).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;geekring&quot;&gt;Geekring&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;geekring.net&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;geekring.net
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;For all those funny, quirky, interesting, personal, strange, offbeat websites that&#x27;s not page 1 on the search engines.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;hotline-webring&quot;&gt;Hotline Webring&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Has no restrictions for joining and may contain objectionable content.&lt;&#x2F;b&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;hotlinewebring.club&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;hotlinewebring.club&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Geekring with more retro themed websites.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-retronaut-webring&quot;&gt;The retronaut webring&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Has no restrictions for joining and may contain objectionable content.&lt;&#x2F;b&gt;&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;webring.dinhe.net&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;webring.dinhe.net&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;A webring that contains all kinds of sites.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-yesterweb-ring&quot;&gt;The Yesterweb Ring&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;yesterweb.org&amp;#x2F;webring&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;yesterweb.org&amp;#x2F;webring&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This webring is for anyone who is tired of how boring and same-y the internet is today. It&#x27;s for anyone who is sick of seeing websites used purely to drive monetization, informative blogs that ask you to subscribe to see content.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;xxiivv-webring&quot;&gt;XXIIVV Webring&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;webring.xxiivv.com&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;webring.xxiivv.com&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This webring is an attempt to inspire artists &amp;amp; developers to build their websites and share traffic amongst each other. The ring welcomes hand-crafted wikis and portfolios.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;the-512kb-club&quot;&gt;The 512KB Club&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;512kb.club&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;512kb.club&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The 512KB Club is a collection of performance-focused web pages from across the Internet.
More of a directory but sometimes considered a webring (it&#x27;s missin the ring-part though)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;sites-with-lots-of-interesting-links&quot;&gt;Sites with lots of interesting links&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;seirdy-s-home&quot;&gt;Seirdy’s Home&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;seirdy.one&amp;#x2F;&quot;&gt;
		https:&amp;#x2F;&amp;#x2F;seirdy.one&amp;#x2F;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Not only a source of great links but also great articles containing them.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>ASCII Table</title>
        <published>2022-10-16T00:00:00+00:00</published>
        <updated>2022-10-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/ascii/"/>
        <id>https://slatecave.net/notebook/ascii/</id>
        
        <summary type="text">A simple, plain ASCII-Table</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/ascii/">&lt;p&gt;This ASCII-Table is mostly derived from the &lt;a href=&quot;https:&#x2F;&#x2F;www.unicode.org&#x2F;Public&#x2F;UCD&#x2F;latest&#x2F;&quot;&gt;Unicode 15.0 Documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mostly-printable-characters&quot;&gt;Mostly Printable Characters&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;#control-characters&quot;&gt;
		Skip to the control characters table
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Codes 0x20 to 0x7E are referred to as the printable characters, 0x7F is a control character. &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ASCII#Printable_characters&quot;&gt;Wikipedia has more background information.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;table class=&quot;character-table&quot;&gt;
&lt;caption&gt;Numbers and Specials&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Char&lt;&#x2F;th&gt;&lt;th&gt;Hex&lt;&#x2F;th&gt;&lt;th&gt;Dec&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SPACE&lt;&#x2F;td&gt;&lt;td&gt;20&lt;&#x2F;td&gt;&lt;td&gt;32&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;!&lt;&#x2F;td&gt;&lt;td&gt;21&lt;&#x2F;td&gt;&lt;td&gt;33&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&quot;&lt;&#x2F;td&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td&gt;34&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#&lt;&#x2F;td&gt;&lt;td&gt;23&lt;&#x2F;td&gt;&lt;td&gt;35&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;$&lt;&#x2F;td&gt;&lt;td&gt;24&lt;&#x2F;td&gt;&lt;td&gt;36&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;%&lt;&#x2F;td&gt;&lt;td&gt;25&lt;&#x2F;td&gt;&lt;td&gt;37&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;&lt;&#x2F;td&gt;&lt;td&gt;26&lt;&#x2F;td&gt;&lt;td&gt;38&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&#x27;&lt;&#x2F;td&gt;&lt;td&gt;27&lt;&#x2F;td&gt;&lt;td&gt;39&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;(&lt;&#x2F;td&gt;&lt;td&gt;28&lt;&#x2F;td&gt;&lt;td&gt;40&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;)&lt;&#x2F;td&gt;&lt;td&gt;29&lt;&#x2F;td&gt;&lt;td&gt;41&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;*&lt;&#x2F;td&gt;&lt;td&gt;2a&lt;&#x2F;td&gt;&lt;td&gt;42&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;+&lt;&#x2F;td&gt;&lt;td&gt;2b&lt;&#x2F;td&gt;&lt;td&gt;43&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;,&lt;&#x2F;td&gt;&lt;td&gt;2c&lt;&#x2F;td&gt;&lt;td&gt;44&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;-&lt;&#x2F;td&gt;&lt;td&gt;2d&lt;&#x2F;td&gt;&lt;td&gt;45&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;.&lt;&#x2F;td&gt;&lt;td&gt;2e&lt;&#x2F;td&gt;&lt;td&gt;46&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&#x2F;&lt;&#x2F;td&gt;&lt;td&gt;2f&lt;&#x2F;td&gt;&lt;td&gt;47&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;30&lt;&#x2F;td&gt;&lt;td&gt;48&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;31&lt;&#x2F;td&gt;&lt;td&gt;49&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;32&lt;&#x2F;td&gt;&lt;td&gt;50&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;33&lt;&#x2F;td&gt;&lt;td&gt;51&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;34&lt;&#x2F;td&gt;&lt;td&gt;52&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;35&lt;&#x2F;td&gt;&lt;td&gt;53&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td&gt;36&lt;&#x2F;td&gt;&lt;td&gt;54&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td&gt;37&lt;&#x2F;td&gt;&lt;td&gt;55&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;8&lt;&#x2F;td&gt;&lt;td&gt;38&lt;&#x2F;td&gt;&lt;td&gt;56&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;9&lt;&#x2F;td&gt;&lt;td&gt;39&lt;&#x2F;td&gt;&lt;td&gt;57&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;:&lt;&#x2F;td&gt;&lt;td&gt;3a&lt;&#x2F;td&gt;&lt;td&gt;58&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;;&lt;&#x2F;td&gt;&lt;td&gt;3b&lt;&#x2F;td&gt;&lt;td&gt;59&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;lt;&lt;&#x2F;td&gt;&lt;td&gt;3c&lt;&#x2F;td&gt;&lt;td&gt;60&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;=&lt;&#x2F;td&gt;&lt;td&gt;3d&lt;&#x2F;td&gt;&lt;td&gt;61&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;gt;&lt;&#x2F;td&gt;&lt;td&gt;3e&lt;&#x2F;td&gt;&lt;td&gt;62&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;3f&lt;&#x2F;td&gt;&lt;td&gt;63&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;table class=&quot;character-table&quot;&gt;
&lt;caption&gt;Uppercase&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Char&lt;&#x2F;th&gt;&lt;th&gt;Hex&lt;&#x2F;th&gt;&lt;th&gt;Dec&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;@&lt;&#x2F;td&gt;&lt;td&gt;40&lt;&#x2F;td&gt;&lt;td&gt;64&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;&#x2F;td&gt;&lt;td&gt;41&lt;&#x2F;td&gt;&lt;td&gt;65&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;B&lt;&#x2F;td&gt;&lt;td&gt;42&lt;&#x2F;td&gt;&lt;td&gt;66&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C&lt;&#x2F;td&gt;&lt;td&gt;43&lt;&#x2F;td&gt;&lt;td&gt;67&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;D&lt;&#x2F;td&gt;&lt;td&gt;44&lt;&#x2F;td&gt;&lt;td&gt;68&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;E&lt;&#x2F;td&gt;&lt;td&gt;45&lt;&#x2F;td&gt;&lt;td&gt;69&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;F&lt;&#x2F;td&gt;&lt;td&gt;46&lt;&#x2F;td&gt;&lt;td&gt;70&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;G&lt;&#x2F;td&gt;&lt;td&gt;47&lt;&#x2F;td&gt;&lt;td&gt;71&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;H&lt;&#x2F;td&gt;&lt;td&gt;48&lt;&#x2F;td&gt;&lt;td&gt;72&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;&#x2F;td&gt;&lt;td&gt;49&lt;&#x2F;td&gt;&lt;td&gt;73&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;J&lt;&#x2F;td&gt;&lt;td&gt;4a&lt;&#x2F;td&gt;&lt;td&gt;74&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;K&lt;&#x2F;td&gt;&lt;td&gt;4b&lt;&#x2F;td&gt;&lt;td&gt;75&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;L&lt;&#x2F;td&gt;&lt;td&gt;4c&lt;&#x2F;td&gt;&lt;td&gt;76&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;M&lt;&#x2F;td&gt;&lt;td&gt;4d&lt;&#x2F;td&gt;&lt;td&gt;77&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;N&lt;&#x2F;td&gt;&lt;td&gt;4e&lt;&#x2F;td&gt;&lt;td&gt;78&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;O&lt;&#x2F;td&gt;&lt;td&gt;4f&lt;&#x2F;td&gt;&lt;td&gt;79&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;P&lt;&#x2F;td&gt;&lt;td&gt;50&lt;&#x2F;td&gt;&lt;td&gt;80&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Q&lt;&#x2F;td&gt;&lt;td&gt;51&lt;&#x2F;td&gt;&lt;td&gt;81&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;R&lt;&#x2F;td&gt;&lt;td&gt;52&lt;&#x2F;td&gt;&lt;td&gt;82&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;S&lt;&#x2F;td&gt;&lt;td&gt;53&lt;&#x2F;td&gt;&lt;td&gt;83&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T&lt;&#x2F;td&gt;&lt;td&gt;54&lt;&#x2F;td&gt;&lt;td&gt;84&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;U&lt;&#x2F;td&gt;&lt;td&gt;55&lt;&#x2F;td&gt;&lt;td&gt;85&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;V&lt;&#x2F;td&gt;&lt;td&gt;56&lt;&#x2F;td&gt;&lt;td&gt;86&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;W&lt;&#x2F;td&gt;&lt;td&gt;57&lt;&#x2F;td&gt;&lt;td&gt;87&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;X&lt;&#x2F;td&gt;&lt;td&gt;58&lt;&#x2F;td&gt;&lt;td&gt;88&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;59&lt;&#x2F;td&gt;&lt;td&gt;89&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Z&lt;&#x2F;td&gt;&lt;td&gt;5a&lt;&#x2F;td&gt;&lt;td&gt;90&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;[&lt;&#x2F;td&gt;&lt;td&gt;5b&lt;&#x2F;td&gt;&lt;td&gt;91&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;\&lt;&#x2F;td&gt;&lt;td&gt;5c&lt;&#x2F;td&gt;&lt;td&gt;92&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;]&lt;&#x2F;td&gt;&lt;td&gt;5d&lt;&#x2F;td&gt;&lt;td&gt;93&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;^&lt;&#x2F;td&gt;&lt;td&gt;5e&lt;&#x2F;td&gt;&lt;td&gt;94&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;_&lt;&#x2F;td&gt;&lt;td&gt;5f&lt;&#x2F;td&gt;&lt;td&gt;95&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;table class=&quot;character-table&quot;&gt;
&lt;caption&gt;Lowercase&lt;&#x2F;caption&gt;
&lt;tr&gt;&lt;th&gt;Char&lt;&#x2F;th&gt;&lt;th&gt;Hex&lt;&#x2F;th&gt;&lt;th&gt;Dec&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;#96;&lt;&#x2F;td&gt;&lt;td&gt;60&lt;&#x2F;td&gt;&lt;td&gt;96&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;a&lt;&#x2F;td&gt;&lt;td&gt;61&lt;&#x2F;td&gt;&lt;td&gt;97&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;b&lt;&#x2F;td&gt;&lt;td&gt;62&lt;&#x2F;td&gt;&lt;td&gt;98&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;c&lt;&#x2F;td&gt;&lt;td&gt;63&lt;&#x2F;td&gt;&lt;td&gt;99&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;d&lt;&#x2F;td&gt;&lt;td&gt;64&lt;&#x2F;td&gt;&lt;td&gt;100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;e&lt;&#x2F;td&gt;&lt;td&gt;65&lt;&#x2F;td&gt;&lt;td&gt;101&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;f&lt;&#x2F;td&gt;&lt;td&gt;66&lt;&#x2F;td&gt;&lt;td&gt;102&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;g&lt;&#x2F;td&gt;&lt;td&gt;67&lt;&#x2F;td&gt;&lt;td&gt;103&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;h&lt;&#x2F;td&gt;&lt;td&gt;68&lt;&#x2F;td&gt;&lt;td&gt;104&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;i&lt;&#x2F;td&gt;&lt;td&gt;69&lt;&#x2F;td&gt;&lt;td&gt;105&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;j&lt;&#x2F;td&gt;&lt;td&gt;6a&lt;&#x2F;td&gt;&lt;td&gt;106&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;k&lt;&#x2F;td&gt;&lt;td&gt;6b&lt;&#x2F;td&gt;&lt;td&gt;107&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;l&lt;&#x2F;td&gt;&lt;td&gt;6c&lt;&#x2F;td&gt;&lt;td&gt;108&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;m&lt;&#x2F;td&gt;&lt;td&gt;6d&lt;&#x2F;td&gt;&lt;td&gt;109&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;n&lt;&#x2F;td&gt;&lt;td&gt;6e&lt;&#x2F;td&gt;&lt;td&gt;110&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;o&lt;&#x2F;td&gt;&lt;td&gt;6f&lt;&#x2F;td&gt;&lt;td&gt;111&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;p&lt;&#x2F;td&gt;&lt;td&gt;70&lt;&#x2F;td&gt;&lt;td&gt;112&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;q&lt;&#x2F;td&gt;&lt;td&gt;71&lt;&#x2F;td&gt;&lt;td&gt;113&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;r&lt;&#x2F;td&gt;&lt;td&gt;72&lt;&#x2F;td&gt;&lt;td&gt;114&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;s&lt;&#x2F;td&gt;&lt;td&gt;73&lt;&#x2F;td&gt;&lt;td&gt;115&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;t&lt;&#x2F;td&gt;&lt;td&gt;74&lt;&#x2F;td&gt;&lt;td&gt;116&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;u&lt;&#x2F;td&gt;&lt;td&gt;75&lt;&#x2F;td&gt;&lt;td&gt;117&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;v&lt;&#x2F;td&gt;&lt;td&gt;76&lt;&#x2F;td&gt;&lt;td&gt;118&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;w&lt;&#x2F;td&gt;&lt;td&gt;77&lt;&#x2F;td&gt;&lt;td&gt;119&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;x&lt;&#x2F;td&gt;&lt;td&gt;78&lt;&#x2F;td&gt;&lt;td&gt;120&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;y&lt;&#x2F;td&gt;&lt;td&gt;79&lt;&#x2F;td&gt;&lt;td&gt;121&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;z&lt;&#x2F;td&gt;&lt;td&gt;7a&lt;&#x2F;td&gt;&lt;td&gt;122&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;{&lt;&#x2F;td&gt;&lt;td&gt;7b&lt;&#x2F;td&gt;&lt;td&gt;123&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;|&lt;&#x2F;td&gt;&lt;td&gt;7c&lt;&#x2F;td&gt;&lt;td&gt;124&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;}&lt;&#x2F;td&gt;&lt;td&gt;7d&lt;&#x2F;td&gt;&lt;td&gt;125&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;~&lt;&#x2F;td&gt;&lt;td&gt;7e&lt;&#x2F;td&gt;&lt;td&gt;126&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DEL&lt;&#x2F;td&gt;&lt;td&gt;7f&lt;&#x2F;td&gt;&lt;td&gt;127&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;&lt;b&gt;Note on the &lt;code&gt;DEL&lt;&#x2F;code&gt; code:&lt;&#x2F;b&gt;The DEL being 0x7F, meaning it has all 7 bits set, probably originates from how one &amp;quot;deleted&amp;quot; something from paper tape, by just punching all of the holes one could &amp;quot;erase&amp;quot; the original character. Its meaning is ambigious, but it usually refers to a backspace.&lt;&#x2F;p&gt;
&lt;p&gt;!note: &lt;b&gt;Fun Fact:&lt;&#x2F;b&gt; The lowercase characters in ASCII were an &amp;quot;aftertought&amp;quot; and only added later in 1965, two years after the initial release.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;control-characters&quot;&gt;Control characters&lt;&#x2F;h2&gt;
&lt;table class=&quot;character-table&quot;&gt;
&lt;tr&gt;&lt;th&gt;Char&lt;&#x2F;th&gt;&lt;th&gt;Hex&lt;&#x2F;th&gt;&lt;th&gt;Oct&lt;&#x2F;th&gt;&lt;th&gt;Dec&lt;&#x2F;th&gt;&lt;th&gt;Name&lt;&#x2F;th&gt;&lt;th&gt;C Esc&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;NUL&lt;&#x2F;td&gt;&lt;td&gt;00&lt;&#x2F;td&gt;&lt;td&gt;000&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Null&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SOH&lt;&#x2F;td&gt;&lt;td&gt;01&lt;&#x2F;td&gt;&lt;td&gt;001&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Start Of Heading&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;STX&lt;&#x2F;td&gt;&lt;td&gt;02&lt;&#x2F;td&gt;&lt;td&gt;002&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Start Of Text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ETX&lt;&#x2F;td&gt;&lt;td&gt;03&lt;&#x2F;td&gt;&lt;td&gt;003&lt;&#x2F;td&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;End Of Text&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;EOT&lt;&#x2F;td&gt;&lt;td&gt;04&lt;&#x2F;td&gt;&lt;td&gt;004&lt;&#x2F;td&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;End Of Transmission&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ENQ&lt;&#x2F;td&gt;&lt;td&gt;05&lt;&#x2F;td&gt;&lt;td&gt;005&lt;&#x2F;td&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Enquiry&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ACK&lt;&#x2F;td&gt;&lt;td&gt;06&lt;&#x2F;td&gt;&lt;td&gt;006&lt;&#x2F;td&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Acknowledge&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;BEL&lt;&#x2F;td&gt;&lt;td&gt;07&lt;&#x2F;td&gt;&lt;td&gt;007&lt;&#x2F;td&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Bell &#x2F; Alert&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\a&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;BS&lt;&#x2F;td&gt;&lt;td&gt;08&lt;&#x2F;td&gt;&lt;td&gt;010&lt;&#x2F;td&gt;&lt;td&gt;8&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;BAckspace&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;HT&lt;&#x2F;td&gt;&lt;td&gt;09&lt;&#x2F;td&gt;&lt;td&gt;011&lt;&#x2F;td&gt;&lt;td&gt;9&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;HOrizontal Tab&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\t&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;LF&lt;&#x2F;td&gt;&lt;td&gt;0a&lt;&#x2F;td&gt;&lt;td&gt;012&lt;&#x2F;td&gt;&lt;td&gt;10&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Line Feed&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\n&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;VT&lt;&#x2F;td&gt;&lt;td&gt;0b&lt;&#x2F;td&gt;&lt;td&gt;013&lt;&#x2F;td&gt;&lt;td&gt;11&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Vertical Tab&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\v&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;FF&lt;&#x2F;td&gt;&lt;td&gt;0c&lt;&#x2F;td&gt;&lt;td&gt;014&lt;&#x2F;td&gt;&lt;td&gt;12&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Form Feed&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\f&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;CR&lt;&#x2F;td&gt;&lt;td&gt;0d&lt;&#x2F;td&gt;&lt;td&gt;015&lt;&#x2F;td&gt;&lt;td&gt;13&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Carriage Return&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\r&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SO&lt;&#x2F;td&gt;&lt;td&gt;0e&lt;&#x2F;td&gt;&lt;td&gt;016&lt;&#x2F;td&gt;&lt;td&gt;14&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Shift Out&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SI&lt;&#x2F;td&gt;&lt;td&gt;0f&lt;&#x2F;td&gt;&lt;td&gt;017&lt;&#x2F;td&gt;&lt;td&gt;15&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Shift In&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DLE&lt;&#x2F;td&gt;&lt;td&gt;10&lt;&#x2F;td&gt;&lt;td&gt;020&lt;&#x2F;td&gt;&lt;td&gt;16&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Data Link Escape&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DC1&lt;&#x2F;td&gt;&lt;td&gt;11&lt;&#x2F;td&gt;&lt;td&gt;021&lt;&#x2F;td&gt;&lt;td&gt;17&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Device Control 1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DC2&lt;&#x2F;td&gt;&lt;td&gt;12&lt;&#x2F;td&gt;&lt;td&gt;022&lt;&#x2F;td&gt;&lt;td&gt;18&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Device Control 2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DC3&lt;&#x2F;td&gt;&lt;td&gt;13&lt;&#x2F;td&gt;&lt;td&gt;023&lt;&#x2F;td&gt;&lt;td&gt;19&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Device Control 3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DC4&lt;&#x2F;td&gt;&lt;td&gt;14&lt;&#x2F;td&gt;&lt;td&gt;024&lt;&#x2F;td&gt;&lt;td&gt;20&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Device Control 4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;NAK&lt;&#x2F;td&gt;&lt;td&gt;15&lt;&#x2F;td&gt;&lt;td&gt;025&lt;&#x2F;td&gt;&lt;td&gt;21&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Negative Acknowledge&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SYN&lt;&#x2F;td&gt;&lt;td&gt;16&lt;&#x2F;td&gt;&lt;td&gt;026&lt;&#x2F;td&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Synchronous Idle&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ETB&lt;&#x2F;td&gt;&lt;td&gt;17&lt;&#x2F;td&gt;&lt;td&gt;027&lt;&#x2F;td&gt;&lt;td&gt;23&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;End Of Transmission Block&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;CAN&lt;&#x2F;td&gt;&lt;td&gt;18&lt;&#x2F;td&gt;&lt;td&gt;030&lt;&#x2F;td&gt;&lt;td&gt;24&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Cancel&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;EM&lt;&#x2F;td&gt;&lt;td&gt;19&lt;&#x2F;td&gt;&lt;td&gt;031&lt;&#x2F;td&gt;&lt;td&gt;25&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;End Of Medium&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SUB&lt;&#x2F;td&gt;&lt;td&gt;1a&lt;&#x2F;td&gt;&lt;td&gt;032&lt;&#x2F;td&gt;&lt;td&gt;26&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Substitute&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ESC&lt;&#x2F;td&gt;&lt;td&gt;1b&lt;&#x2F;td&gt;&lt;td&gt;033&lt;&#x2F;td&gt;&lt;td&gt;27&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Escape&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;\e&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;FS&lt;&#x2F;td&gt;&lt;td&gt;1c&lt;&#x2F;td&gt;&lt;td&gt;034&lt;&#x2F;td&gt;&lt;td&gt;28&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;File Seperator&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;GS&lt;&#x2F;td&gt;&lt;td&gt;1d&lt;&#x2F;td&gt;&lt;td&gt;035&lt;&#x2F;td&gt;&lt;td&gt;29&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Group Seperator&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;RS&lt;&#x2F;td&gt;&lt;td&gt;1e&lt;&#x2F;td&gt;&lt;td&gt;036&lt;&#x2F;td&gt;&lt;td&gt;30&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Record Seperator&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;US&lt;&#x2F;td&gt;&lt;td&gt;1f&lt;&#x2F;td&gt;&lt;td&gt;037&lt;&#x2F;td&gt;&lt;td&gt;31&lt;&#x2F;td&gt;&lt;td class=&quot;name&quot;&gt;Unit Seperator&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; The Names have been titlecased to make them easier to read.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;notation&quot;&gt;Notation&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;escaping-in-scripting-and-programming&quot;&gt;Escaping in Scripting and Programming&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes in languages one can&#x27;t use all the characters available in the source file, especially when using control characters. Encoding these as other characters is called &lt;i&gt;escaping&lt;&#x2F;i&gt;. This usually happens with text between double quotes &lt;code&gt;&amp;quot;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are differences between languages and implementations but the general rules are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Special characters preceeded by a backslash &lt;code&gt;\&lt;&#x2F;code&gt; are taken literally. (i.e. &lt;code&gt;\&amp;quot;&lt;&#x2F;code&gt; or &lt;code&gt;\\&lt;&#x2F;code&gt;) (The exact behaviour varies &lt;em&gt;a lot&lt;&#x2F;em&gt; by language.)&lt;&#x2F;li&gt;
&lt;li&gt;Latin characters preceeded by a backslash to generate control characters (i.e. &lt;code&gt;\n&lt;&#x2F;code&gt;) (The &lt;i&gt;C Esc&lt;&#x2F;i&gt; column)&lt;&#x2F;li&gt;
&lt;li&gt;Hexadecimal value of the character prefixed by &lt;code&gt;\x&lt;&#x2F;code&gt; (i.e. &lt;code&gt;0x1b&lt;&#x2F;code&gt;) (should work almost everywhere)&lt;&#x2F;li&gt;
&lt;li&gt;Octal encoding prefixed by just a backslash (i.e. &lt;code&gt;\033&lt;&#x2F;code&gt;) (&lt;a href=&quot;&#x2F;notebook&#x2F;ansi-escape-sequences&#x2F;#setting-text-color-and-effects&quot;&gt;Use this when using &lt;code&gt;printf&lt;&#x2F;code&gt; on the command line&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; &lt;code&gt;C Esc&lt;&#x2F;code&gt; here is short C-Escape as the C language apparantly started this way of excaping characters in strings. It is now used by almost all modern languages and in other contexts. i.e. &lt;code&gt;printf&lt;&#x2F;code&gt;, &lt;code&gt;sed&lt;&#x2F;code&gt;, etc. exact support may vary.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;escaping-in-xml-and-html&quot;&gt;Escaping in XML and HTML&lt;&#x2F;h3&gt;
&lt;p&gt;XML and html have their own way of escaping non-printable characters, this involves a sequence sandwiched between an ampersand &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt; and a semicolon &lt;code&gt;;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In general Characters can be escaped using &lt;code&gt;&amp;amp;#&amp;lt;dec&amp;gt;;&lt;&#x2F;code&gt; where &lt;code&gt;&amp;lt;dec&amp;gt;&lt;&#x2F;code&gt; is replaced by the decimal value associted with the character. (i.e, &lt;code&gt;&amp;amp;#38;&lt;&#x2F;code&gt; to encode an ampersand &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;There are also named escapes to make remembering them easier:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Character&lt;&#x2F;th&gt;&lt;th&gt;XML-escape&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;amp;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&amp;amp;amp;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;lt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&amp;amp;lt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&amp;amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&amp;amp;quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;h3 id=&quot;control-and-shift&quot;&gt;Control and Shift&lt;&#x2F;h3&gt;
&lt;p&gt;With the ASCII table control and shift keys an be implemented using simple addition and substraction.&lt;&#x2F;p&gt;
&lt;p&gt;The lowercase character can be obtained by adding 32 (0x20) to the code of the corresponding uppercase character and the control key goes 64 (0x40) in the opposite direction.&lt;&#x2F;p&gt;
&lt;p&gt;With that &lt;kbd&gt;ctrl+c&lt;&#x2F;kbd&gt; maps to code 3 &amp;quot;End of Text&amp;quot;. And &lt;kbd&gt;ctrl+d&lt;&#x2F;kbd&gt; maps to &amp;quot;End of Transmission&amp;quot;. You may know those shortcuts from the terminal, they hopefully make a bit more sense now.&lt;&#x2F;p&gt;
&lt;p&gt;Control characters are sometimes written down&#x2F;printed as the character one gets when adding 64 to the control characters value prefixed by an &lt;code&gt;^&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This maps escape to &lt;code&gt;^]&lt;&#x2F;code&gt; and the nullbyte to &lt;code&gt;^@&lt;&#x2F;code&gt;. (You have probably seen those when opening a binary file in a text editor.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unicode&quot;&gt;Unicode&lt;&#x2F;h2&gt;
&lt;p&gt;Creating a unicode table andkeeping it updated is out of scope here, besides that: &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;List_of_Unicode_characters&quot;&gt;Wikipedia has a List of Unicode Characters&lt;&#x2F;a&gt; and there are the &lt;a href=&quot;https:&#x2F;&#x2F;www.unicode.org&#x2F;charts&#x2F;&quot;&gt;offical Unicode Character Code Charts&lt;&#x2F;a&gt;. (Plus a whole lot of other unicode tables out there.)&lt;&#x2F;p&gt;
&lt;p&gt;There is also a little commandline tool called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arp242&#x2F;uni&quot;&gt;&lt;code&gt;uni&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, that is pretty good at providing a searchable unicode table.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Shell Snippets</title>
        <published>2022-10-16T00:00:00+00:00</published>
        <updated>2023-05-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/notebook/shell/"/>
        <id>https://slatecave.net/notebook/shell/</id>
        
        <summary type="text">Some shellcode I catch myself writing over and over again.</summary>
        
        <content type="html" xml:base="https://slatecave.net/notebook/shell/">&lt;p&gt;The snippets on this site are intended for getting copied and being useful so please do that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;argument-parser&quot;&gt;Argument Parser&lt;&#x2F;h2&gt;
&lt;p&gt;A simple argument parser for bash.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;!&#x2F;bin&#x2F;bash&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Make the script fail when a command fails&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-set z-shell&quot;&gt;set&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;-&lt;&#x2F;span&gt;e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Settings variables go here&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;FOO&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;foo&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-function z-shell&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-shell&quot;&gt;show_help&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;cat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-unquoted z-heredoc z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-redirection z-shell&quot;&gt;&amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control z-heredoc-token z-shell&quot;&gt;EOF&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-heredoc z-shell&quot;&gt;
Usage: foo &amp;lt;&amp;lt;options&amp;gt;&amp;gt; &amp;lt;bar&amp;gt;

Blubber blah blah ...
&lt;span class=&quot;z-keyword z-control z-heredoc-token z-shell&quot;&gt;EOF&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-keyword z-control z-loop z-while z-shell&quot;&gt;while&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-double-brace z-begin z-shell&quot;&gt;[[&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;-&lt;&#x2F;span&gt;gt&lt;&#x2F;span&gt; 0 &lt;span class=&quot;z-support z-function z-double-brace z-end z-shell&quot;&gt;]]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-continue z-shell&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-control z-loop z-do z-shell&quot;&gt;do&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-conditional z-case z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-control z-conditional z-case z-shell&quot;&gt;case&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-control z-in z-shell&quot;&gt;in&lt;&#x2F;span&gt;
		&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-patterns z-shell&quot;&gt;--foo&lt;span class=&quot;z-meta z-conditional z-case z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-control z-conditional z-patterns z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-commands z-shell&quot;&gt;
			&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;FOO&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
			&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-shift z-shell&quot;&gt;shift&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-commands z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-terminator z-case z-clause z-shell&quot;&gt;;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-patterns z-shell&quot;&gt;
		
		&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-patterns z-shell&quot;&gt;--help&lt;span class=&quot;z-meta z-conditional z-case z-clause z-patterns z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-control z-conditional z-patterns z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-commands z-shell&quot;&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;show_help&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-continue z-shell&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-exit z-shell&quot;&gt;exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-commands z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-terminator z-case z-clause z-shell&quot;&gt;;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-patterns z-shell&quot;&gt;
		&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-patterns z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-regexp z-quantifier z-shell&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-patterns z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-control z-conditional z-patterns z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-commands z-shell&quot;&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;Unknown option: %s\n&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-logical z-continue z-shell&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-exit z-shell&quot;&gt;exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-commands z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-terminator z-case z-clause z-shell&quot;&gt;;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-conditional z-case z-clause z-patterns z-shell&quot;&gt;
	&lt;span class=&quot;z-keyword z-control z-conditional z-end z-shell&quot;&gt;esac&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-control z-loop z-end z-shell&quot;&gt;done&lt;&#x2F;span&gt;

&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Logic goes here&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-echo z-shell&quot;&gt;echo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;FOO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;set-e-and-shift&quot;&gt;&lt;code&gt;set -e&lt;&#x2F;code&gt; and &lt;code&gt;shift&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;set -e&lt;&#x2F;code&gt; option tells the shell to stop executing a script once a command fails. This is important when using a &lt;code&gt;shift&lt;&#x2F;code&gt; in this kind of while loop where the loop exiting depends on the &lt;code&gt;shift&lt;&#x2F;code&gt; working.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Example:&lt;&#x2F;b&gt; The shift may fail if it is told to move 2 arguments out (&lt;code&gt;shift 2&lt;&#x2F;code&gt;) but there is only one given. In the example above that would be fulfilled when passing &lt;code&gt;--foo&lt;&#x2F;code&gt; as the last argument (a relatively easy to make mistake). If the shift silently failed the argument counter would never reach 0 and the script would be stuck in an endless loop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hook-helper&quot;&gt;Hook Helper&lt;&#x2F;h2&gt;
&lt;p&gt;This little function calls its argument as an ad-hoc shellscript if it is non-empty, if it returns a nonzero, one should call the default action.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; on fail do default action&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-shell&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-shell&quot;&gt;run_hook&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-support z-function z-test z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;-&lt;&#x2F;span&gt;n&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-test z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-and z-shell&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;c&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example of how this little helper is intended to be used&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-storage z-modifier z-shell&quot;&gt;export&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;VARIABLE_WHICH_IS_USEFUL_FOR_HOOK&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;something&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;run_hook&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;HOOK_COMMAND&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-or z-shell&quot;&gt;||&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;default_action&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;An &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;branch&#x2F;main&#x2F;.local&#x2F;bin&#x2F;filedialog&quot;&gt;example of this being used&lt;&#x2F;a&gt; can be found in my dotfiles with the filedialog. (Fun fact: It was created for this script)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;grep-regex-match-helper&quot;&gt;Grep Regex Match Helper&lt;&#x2F;h2&gt;
&lt;p&gt;This function pipes its first argument into a &lt;code&gt;grep -q&lt;&#x2F;code&gt; command which is given the rest of the arguments.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function z-shell&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-shell&quot;&gt;matches&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-shift z-shell&quot;&gt;shift&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; 1&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;%s\n&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;grep&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;q&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-shell&quot;&gt;@&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Example usage: &lt;code&gt;matches &amp;quot;$FOO&amp;quot; &amp;quot;^[0-9]$&amp;quot; &amp;amp;&amp;amp; echo &amp;quot;FOO is a number! :)&amp;quot; || FOO=0&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It can also be useful to implement some kind of heuristics based on which patterns occur in a text.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bash-arrays&quot;&gt;Bash Arrays&lt;&#x2F;h2&gt;
&lt;p&gt;Bash has arrays so one doesn&#x27;t have to care about delimiters too much.&lt;&#x2F;p&gt;
&lt;p&gt;Create an array:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;empty_array&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;
&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;array&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;text&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;can go&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-command z-parens z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-echo z-shell&quot;&gt;echo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; here &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Appending to an exisiting Array:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;array&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;+=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;foo&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;
&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;array&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;+=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;add&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;multiple&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;elements&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These can then be accessed with 0 based indices:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-echo z-shell&quot;&gt;echo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;array&lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; prints out: can go&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To get the number of elements in an array:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-echo z-shell&quot;&gt;echo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;array&lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-array z-shell&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-echo z-shell&quot;&gt;echo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;array&lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-array z-shell&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;An array can expand to multiple arguments:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-&amp;gt; %s&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;array&lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-array z-shell&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Compare with the output of:&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-printf z-shell&quot;&gt;printf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-&amp;gt; %s&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; foo bar baz&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which is also useful for &lt;code&gt;for&lt;&#x2F;code&gt;-loops:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-keyword z-control z-loop z-for z-shell&quot;&gt;for&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-for z-shell&quot;&gt; i &lt;span class=&quot;z-keyword z-control z-in z-shell&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-begin z-shell&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;array&lt;span class=&quot;z-punctuation z-section z-braces z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-array z-shell&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-braces z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-expansion z-parameter z-end z-shell&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-continue z-shell&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-control z-loop z-do z-shell&quot;&gt;do&lt;&#x2F;span&gt;
	&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-echo z-shell&quot;&gt;echo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-&amp;gt; &lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;i&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-control z-loop z-end z-shell&quot;&gt;done&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And to clean up:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; remove element at index 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;uset&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; array&lt;span class=&quot;z-keyword z-control z-regexp z-set z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;1&lt;span class=&quot;z-keyword z-control z-regexp z-set z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; remove the whole array&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;uset&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; array&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There is more to bash arrays (including associative arrays) … &lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;www.gnu.org&amp;#x2F;software&amp;#x2F;bash&amp;#x2F;manual&amp;#x2F;html_node&amp;#x2F;Arrays.html&quot;&gt;
		Check out The Bash Reference Manual on Arrays
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;see-also&quot;&gt;See Also&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;&amp;#x2F;notebook&amp;#x2F;ansi-escape-sequences&quot;&gt;
		ANSI Escape Sequences
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Having a look at greetd</title>
        <published>2022-08-16T00:00:00+00:00</published>
        <updated>2023-03-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/a-look-at-greetd/"/>
        <id>https://slatecave.net/blog/a-look-at-greetd/</id>
        
        <summary type="text">A writeup of the internals of the greetd login manager</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/a-look-at-greetd/">&lt;h2 id=&quot;what-is-greetd&quot;&gt;What is greetd?&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;greetd is a minimal and flexible login manager daemon that makes no assumptions about what you want to launch.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In other words &lt;a href=&quot;https:&#x2F;&#x2F;kl.wtf&#x2F;&quot;&gt;Kenny Levinsen&lt;&#x2F;a&gt; created an awesome piece of software that allows anyone to easily launch &lt;em&gt;anything&lt;&#x2F;em&gt; on login and that without being bound to a fronted.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&quot;&gt;
		You can find greetd on sourcehut
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Most information in this article came from reading the greetd code and making notes on good old paper.&lt;&#x2F;p&gt;
&lt;p&gt;Note: Kenny Levinsen also wrote &lt;a href=&quot;https:&#x2F;&#x2F;kl.wtf&#x2F;posts&#x2F;2022&#x2F;03&#x2F;12&#x2F;login-managers-an-introduction.html&quot;&gt;an interesting blogpost on login managers.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt;
If you want to understand how greetd works please read the code yourself,
this article alone is not enough!&lt;br &#x2F;&gt;
(but hopefully helpful)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;about&#x2F;me&#x2F;#contact&quot;&gt;In case you find a mistake or have feedback please contact me.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;greetd-structure&quot;&gt;How greetd is structured&lt;&#x2F;h2&gt;
&lt;p&gt;Greetd has a pretty modular approach to things and is made of a bunch of components one can look at separately.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The managing greetd server daemon (main focus)&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;greetd --session-worker&lt;&#x2F;code&gt; process (good to know)&lt;&#x2F;li&gt;
&lt;li&gt;The greeter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I won&#x27;t be looking at the greeter as the interface is pretty well &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;tree&#x2F;master&#x2F;item&#x2F;man&#x2F;greetd-ipc-7.scd&quot;&gt;described in the manual&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The other two are both compiled into the same binary, the main function decides which component it should become after parsing the command line parameters.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;greetd-common&quot;&gt;The common part&lt;&#x2F;h2&gt;
&lt;p&gt;There is a small common part that always happens when the greetd binary starts up:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It invokes the commandline and configuration parser to get its complete configuration.&lt;&#x2F;li&gt;
&lt;li&gt;It invokes &lt;code&gt;&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;mlockall&quot;&gt;mlockall()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; to prevent sensitive data like buffers containing passwords from being swapped to disk.&lt;&#x2F;li&gt;
&lt;li&gt;Then it decides what to become.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;greetd-session-worker&quot;&gt;The greetd session worker&lt;&#x2F;h2&gt;
&lt;p&gt;The simpler part of greetd is the actual session worker, so I&#x27;ll start with that …&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;session&amp;#x2F;worker.rs#L84&quot;&gt;
		The relevant &lt;code&gt;worker()&lt;&#x2F;code&gt; function in &lt;code&gt;greetd&#x2F;src&#x2F;session&#x2F;worker.rs&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;The session worker is started by starting greetd with the &lt;code&gt;--session-worker&lt;&#x2F;code&gt; or simply &lt;code&gt;-w&lt;&#x2F;code&gt; flag and passing the number of a file descriptor that is supposed to be a Unix socket for exchanging commands and feedback.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greetd-session-worker-function&quot;&gt;What it does&lt;&#x2F;h3&gt;
&lt;p&gt;The session worker itself is one strand of pretty straightforward code that feels a bit like a shellscript that asks for input and aborts with an error message on failure.&lt;&#x2F;p&gt;
&lt;p&gt;It does a few things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Wait for an &lt;code&gt;InitiateLogin&lt;&#x2F;code&gt; or &lt;code&gt;Cancel&lt;&#x2F;code&gt; message.&lt;&#x2F;li&gt;
&lt;li&gt;Setup a conversation with &lt;abbr title=&quot;Pluggable Authentication Modules&quot;&gt;PAM&lt;&#x2F;abbr&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Let a &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;tree&#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&#x2F;item&#x2F;greetd&#x2F;src&#x2F;session&#x2F;conv.rs&quot;&gt;PAM adaptor implemented in a separate file&lt;&#x2F;a&gt; do the authentication conversation.&lt;&#x2F;li&gt;
&lt;li&gt;Do &lt;code&gt;&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pam_acct_mgmt&quot;&gt;pam_acct_mgmt()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; and &lt;code&gt;&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;pam_setcred&quot;&gt;pam_setcred()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; and send a &lt;code&gt;Success&lt;&#x2F;code&gt; message.&lt;&#x2F;li&gt;
&lt;li&gt;Wait for a &lt;code&gt;Args&lt;&#x2F;code&gt; (sets the command for ) or &lt;code&gt;Cancel&lt;&#x2F;code&gt; message and answer &lt;code&gt;Success&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Wait for a &lt;code&gt;Start&lt;&#x2F;code&gt; or &lt;code&gt;Cancel&lt;&#x2F;code&gt; message.&lt;&#x2F;li&gt;
&lt;li&gt;Fetch user information from PAM.&lt;&#x2F;li&gt;
&lt;li&gt;Set a session id using &lt;code&gt;&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;setsid.2&quot;&gt;setsid()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; to make the process the root of a new session.&lt;&#x2F;li&gt;
&lt;li&gt;If we have a specific virtual-terminal configured.
&lt;ol&gt;
&lt;li&gt;Tell PAM that we want to run in that tty.&lt;&#x2F;li&gt;
&lt;li&gt;Set the &lt;code&gt;XDG_VTNR&lt;&#x2F;code&gt; environment variable via PAM.&lt;&#x2F;li&gt;
&lt;li&gt;Open the terminal file, set the tty to text mode, clear it and switch to it if necessary.&lt;&#x2F;li&gt;
&lt;li&gt;Use the greetd terminal abstraction to &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;tree&#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&#x2F;item&#x2F;greetd&#x2F;src&#x2F;terminal&#x2F;mod.rs#L204&quot;&gt;do the plumbing for the pipes&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;tree&#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&#x2F;item&#x2F;greetd&#x2F;src&#x2F;terminal&#x2F;mod.rs#L229&quot;&gt;switch the controlling tty&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Set the &lt;abbr title=&quot;Present Working Directory&quot;&gt;pwd&lt;&#x2F;abbr&gt; for the new session, either to the users home or the filesystem root as fallback.&lt;&#x2F;li&gt;
&lt;li&gt;Set a whole bunch of environment variables for the session (including the &lt;code&gt;GREETD_SOCK&lt;&#x2F;code&gt; for the greeter, and the ones received with the &lt;code&gt;Args&lt;&#x2F;code&gt; message) using PAM.&lt;&#x2F;li&gt;
&lt;li&gt;Prepare the command to execute in the new session (shell + profile files + session command)&lt;&#x2F;li&gt;
&lt;li&gt;Fetch the environment variables for the new session from PAM.&lt;&#x2F;li&gt;
&lt;li&gt;Fork&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;After the fork the parent sends the &lt;abbr title=&quot;Process ID&quot;&gt;pid&lt;&#x2F;abbr&gt; to the the greetd server process and closes the socket, sets its &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;prctl.2#PR_SET_PDEATHSIG&quot;&gt;parent death signal&lt;&#x2F;a&gt; to &lt;code&gt;SIGTERM&lt;&#x2F;code&gt; and waits for the child thread to terminate before ending the PAM session.&lt;&#x2F;p&gt;
&lt;p&gt;The child process drops its privileges, also sets its &lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;prctl.2#PR_SET_PDEATHSIG&quot;&gt;parent death signal&lt;&#x2F;a&gt; and then uses &lt;code&gt;&lt;a href=&quot;https:&#x2F;&#x2F;man.voidlinux.org&#x2F;execve.2&quot;&gt;execve()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; to launch the prepared command with the environment provided by PAM.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note on Environment variables:&lt;&#x2F;b&gt; The variables received using the &lt;code&gt;Args&lt;&#x2F;code&gt; call get set before the ones coming from the greeter to avoid overriding some important variables. &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;commit&#x2F;bc7e369225b628f0fb4863c9cfc4f48a3d632d41&quot;&gt;This behaviour was introduced&lt;&#x2F;a&gt; after &lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;commit&#x2F;2f1810d1a246ee61c7f9334946eb8ed0ee40a80d&quot;&gt;making it possible for the greeter to set the environment variables (again)&lt;&#x2F;a&gt;.
&lt;b&gt;Personal opinion:&lt;&#x2F;b&gt; If you have a good reason for changing those you shouldn&#x27;t be doing that through the greeter anyway.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;greeter-protocol&quot;&gt;Messages between Session Worker and Server&lt;&#x2F;h2&gt;
&lt;p&gt;For later reference here are the messages that are exchanged between session worker and server via given socket using a JSON based protocol.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;parent-to-session-messages&quot;&gt;Parent to Session Messages&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;initiate-login-message&quot;&gt;&lt;code&gt;InitiateLogin&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Message sent to start the login process.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		string service
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The PAM service id so that PAM knows which configuration to apply
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string class
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		ends up in the &lt;code&gt;XDG_SESSION_CLASS&lt;&#x2F;code&gt;, either &#x27;user&#x27; or &#x27;greeter&#x27;
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string user
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		the username to log in as
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		bool authenticate
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		set to &lt;code&gt;false&lt;&#x2F;code&gt; if we have an autologin session
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		TerminalMode tty
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The description of the terminal to attach to
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		bool source_profile
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Whether or not to read the profile files before executing the session command
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;pam-response-message&quot;&gt;&lt;code&gt;PamResponse&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;A response to a question from PAM.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		string? response
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The response, may be null
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;args-message&quot;&gt;&lt;code&gt;Args&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sets the command to be executed for the session when &lt;code&gt;Start&lt;&#x2F;code&gt; is sent.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		string[] cmd
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		An array of arguments representing the command
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string[] env
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		An array of environment variables to pass to the given command (&lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;commit&#x2F;2f1810d1a246ee61c7f9334946eb8ed0ee40a80d&quot;&gt;it was added (back) on 2022-08-13&lt;&#x2F;a&gt;)
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;start-message&quot;&gt;&lt;code&gt;Start&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Gives the final go after the session is full set up.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;cancel-message&quot;&gt;&lt;code&gt;Cancel&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Can be used at almost any step to cancel the session setup.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;session-to-parent-messages&quot;&gt;Session to Parent Messages&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;success-message&quot;&gt;&lt;code&gt;Success&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;What it says on the tin. Signals that a command has successfully finished.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;error-message&quot;&gt;&lt;code&gt;Error&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Signals that an error has been caught somewhere.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		Error error
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		A rusty error response, probably gets serialised to an error message
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;pam-message-message&quot;&gt;&lt;code&gt;PamMessage&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;A Question or other Output from PAM.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		AuthMsgType style
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		an enum telling what kind of message this is and what the expected response is, one of Visible, Secret, Info or Error
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string msg
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		a string intended for the human trying to log in
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h4 id=&quot;final-child-pid-message&quot;&gt;&lt;code&gt;FinalChildPid&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sends back the process id of the session child after the session was launched.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		uint pid
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		the process id
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;greetd-server&quot;&gt;The greetd server&lt;&#x2F;h2&gt;
&lt;p&gt;The server process is the one started by the init-system, it is responsible for launching the session workers and telling them what to do, it also houses the internal state machine that knows which session to launch next, it also does some &amp;quot;managing&amp;quot; for the processes in the session.&lt;&#x2F;p&gt;
&lt;p&gt;The server is made up of multiple components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A base to orchestrate the startup and running sequence, also handles signals&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;code&gt;Context&lt;&#x2F;code&gt; and and &lt;code&gt;ContextInner&lt;&#x2F;code&gt; to keep track of multiple sessions and scheduling&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Session&lt;&#x2F;code&gt; objects that launch the session workers and handle the communication&lt;&#x2F;li&gt;
&lt;li&gt;A function that does the communicating with the greeter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;greetd-server-function&quot;&gt;What it does …&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;greetd-server-function-startup&quot;&gt;… on startup&lt;&#x2F;h4&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;server.rs#L201&quot;&gt;
		The entry point is the &lt;code&gt;main()&lt;&#x2F;code&gt; function in the &lt;code&gt;greetd&#x2F;src&#x2F;server.rs&lt;&#x2F;code&gt; file
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;After the initialisation has decided that it will be a greetd server the main function also does a few things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Determine the service name it will tell PAM based on which configurations for PAM are available. (warns when using the &lt;code&gt;login&lt;&#x2F;code&gt; fallback)&lt;&#x2F;li&gt;
&lt;li&gt;Get some info about the user configured for the &lt;code&gt;default_session&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Create the Unix Socket (&lt;code&gt;Listener&lt;&#x2F;code&gt;) for the greeter, set permissions and set the &lt;code&gt;GREETD_SOCK&lt;&#x2F;code&gt; environment variable.&lt;&#x2F;li&gt;
&lt;li&gt;Get information about the tty it is supposed to be on.
Returns an error told to wait for the terminal to become active it waits (&lt;code&gt;terminal.switch&lt;&#x2F;code&gt; set to &lt;code&gt;false&lt;&#x2F;code&gt; in config).&lt;&#x2F;li&gt;
&lt;li&gt;Create a &lt;code&gt;Context&lt;&#x2F;code&gt; to do the housekeeping for us.&lt;&#x2F;li&gt;
&lt;li&gt;Start initial session or greeter for the default session depending on which one is configured. (Other state transitions after initial bootstrapping are handled by the &lt;code&gt;Context&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;Create the runfile to know the initial run apart form the others in terms of system restarts, not greetd restarts.&lt;&#x2F;li&gt;
&lt;li&gt;Loop to handle posix signals (&lt;code&gt;SIGALRM&lt;&#x2F;code&gt;, &lt;code&gt;SIGCHLD&lt;&#x2F;code&gt;, &lt;code&gt;SIGTERM&lt;&#x2F;code&gt; and &lt;code&gt;SIGINT&lt;&#x2F;code&gt;) as well as Incoming connections on the greeter socket.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;greetd-server-function-greeter&quot;&gt;… on an incoming connection from the greeter&lt;&#x2F;h4&gt;
&lt;p&gt;The connections are accepted using the main-loop (see previous section) the handler gets the bidirectional datagram stream and a copy of the &lt;code&gt;Context&lt;&#x2F;code&gt; structure (the inner context staying the same, this is how the information about the current global state is exchanged between possibly multiple connections).&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;server.rs#L69&quot;&gt;
		The &lt;code&gt;client_handler()&lt;&#x2F;code&gt; in &lt;code&gt;greetd&#x2F;src&#x2F;server.rs&lt;&#x2F;code&gt; handles the communication
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;Inside this function is a loop that reads the request and then decides which function to call on the &lt;code&gt;Context&lt;&#x2F;code&gt;, wraps up the response and sends it back.&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		CreateSession
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls the &lt;code&gt;create_session()&lt;&#x2F;code&gt; function with the given username, if successful, responds with the first question
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		PostAuthMessageResponse
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls the &lt;code&gt;post_response()&lt;&#x2F;code&gt; function to answer the PAM question, fetches and returns the next question or the &lt;code&gt;Ready&lt;&#x2F;code&gt; response if that didn&#x27;t fail
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		StartSession
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls the &lt;code&gt;start()&lt;&#x2F;code&gt; function and returns the result
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		CancelSession
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls &lt;code&gt;cancel()&lt;&#x2F;code&gt; function and returns the result
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;There are two helper functions:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		client_get_question(context)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This function fetches the next question from the context using the &lt;code&gt;get_question()&lt;&#x2F;code&gt; method, if it returns a question it packs that into an object that can be serialised and sent back, if not it calls &lt;code&gt;wrap_result()&lt;&#x2F;code&gt; on the result and returns that, meaning automatic error handling or an automatic success message in case of an ok.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		wrap_result(result)
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This function takes a &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;result&#x2F;enum.Result.html&quot;&gt;Result&lt;&#x2F;a&gt; and spits out a spendable success or error message containing appropriate information about the error.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;greetd-context&quot;&gt;Context&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;context.rs&quot;&gt;
		The &lt;code&gt;Context&lt;&#x2F;code&gt; mechanism is implemented in &lt;code&gt;greetd&#x2F;src&#x2F;context.rs&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;As already stated greetd manages its state using &lt;code&gt;Context&lt;&#x2F;code&gt; objects (no idea what the correct rust terminology is, but its a data-structure with a set of functions and quacks like an object). To be more precise it uses two context objects, the outer &lt;code&gt;Context&lt;&#x2F;code&gt; for configuration and connection state and the &lt;code&gt;ContextInner&lt;&#x2F;code&gt; for storing global state of a 3-stage session pipeline that is shared between all interested parts of greetd.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greed-context-struct&quot;&gt;Datastructures&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;Context&lt;&#x2F;code&gt; object holds the following values:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		&lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;struct.RwLock.html&quot;&gt;RwLock&lt;&#x2F;a&gt;&amp;lt;ContextInner&amp;gt; inner
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;code&gt;ContextInner&lt;&#x2F;code&gt; object for global state wrapped in a lock that allows shared reading or exclusive writing
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string greeter_bin
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The command for the greeter
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string greeter_user
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The user the greeter runs as
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string greeter_service
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The PAM service name for starting greeter sessions
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string pam_service
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The PAM service name for starting normal sessions 
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		TerminalMode term_mode
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The vt in which this context spawns sessions
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		bool source_profile
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The &lt;code&gt;source_profile&lt;&#x2F;code&gt; setting
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		string runfile
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The path of the runfile for persisting sate across possible greetd restarts
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The &lt;code&gt;ContextInner&lt;&#x2F;code&gt; has the following fields:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		SessionChildSet? current
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Information about the currently running session
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		SessionSet? scheduled
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The session that will be started once the currently running session terminates
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		SessionSet? configuring
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Stage before &lt;code&gt;scheduled&lt;&#x2F;code&gt;, a session that isn&#x27;t ready to be launched just yet because the greeter is still answering the questions PAM has
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The &lt;code&gt;SessionSet&lt;&#x2F;code&gt; here is a &lt;code&gt;Session&lt;&#x2F;code&gt; (representing a session that is being set up) with a timestamp attached. The &lt;code&gt;SessionChildSet&lt;&#x2F;code&gt; stores a &lt;code&gt;SessionChild&lt;&#x2F;code&gt; (representing a running session) and has an &lt;code&gt;is_greeter&lt;&#x2F;code&gt; flag in addition to the timestamp.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;&#x2F;b&gt; There is a distinction between the &lt;code&gt;Session&lt;&#x2F;code&gt; and &lt;code&gt;SessionChild&lt;&#x2F;code&gt; object because a session  that is already running doesn&#x27;t care about all of the communications foo and is only interested in finding out if the session is still running and ending it if it isn&#x27;t supposed to be running anymore, the only shared value is the pid of the session worker.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greetd-context-methods&quot;&gt;Methods&lt;&#x2F;h3&gt;
&lt;p&gt;The methods on the context seem to be made to make it easy to write down what should happen from the outside, also they are pretty well commented so you know how they are supposes to be hooked up.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-start-unauthenticated-session&quot;&gt;&lt;code&gt;start_unauthenticated_session()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Directly start an unauthenticated session, bypassing the normal scheduling. &lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This one translates to spawning a session worker and sending it all commands that should be needed for an autologin session and gives empty answers for any questions PAM asks. This is used for the &lt;code&gt;initial_session&lt;&#x2F;code&gt; and the sessions for the greeters. It returns the created &lt;code&gt;Session&lt;&#x2F;code&gt; as an object. Username, session-class, PAM-service and the command are given as arguments.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-start-greeter&quot;&gt;&lt;code&gt;start_greeter()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Directly start a greeter session, bypassing the normal scheduling.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;A small convenience wrapper that preconfigures the &lt;code&gt;start_unauthenticated_session()&lt;&#x2F;code&gt; with stored settings for a greeter session, uses the &lt;code&gt;greeter_service&lt;&#x2F;code&gt; as the PAM service. It also returns the created &lt;code&gt;Session&lt;&#x2F;code&gt; object.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-greet&quot;&gt;&lt;code&gt;greet()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Directly start a greeter session, bypassing the normal scheduling.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This one starts a greeter session and sets it as the &lt;code&gt;current&lt;&#x2F;code&gt; session in the inner context. It returns an error if that &lt;code&gt;current&lt;&#x2F;code&gt; session is already occupied.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-start-user-session&quot;&gt;&lt;code&gt;start_user_session()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Directly start an initial session, bypassing the normal scheduling.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This will use &lt;code&gt;start_unauthenticated_session()&lt;&#x2F;code&gt; to autologin a given user with the given command. (used to implement the &lt;code&gt;initial_session&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-create-session&quot;&gt;&lt;code&gt;create_session()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a new session for configuration.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Creates a session preconfigured with the Context settings and the given username and swaps it into &lt;code&gt;configuring&lt;&#x2F;code&gt;.
Also mitigates a race-condition by cancelling a swapped out session.&lt;&#x2F;p&gt;
&lt;p&gt;Returns an error if there is no &lt;code&gt;current&lt;&#x2F;code&gt; session or there already is a session &lt;code&gt;configuring&lt;&#x2F;code&gt; or &lt;code&gt;scheduled&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-cancel&quot;&gt;&lt;code&gt;cancel()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Cancel the session being configured.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h4 id=&quot;greetd-context-methods-get-question&quot;&gt;&lt;code&gt;get_question()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Retrieve a question from the session under configuration.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Uses the &lt;code&gt;get_state()&lt;&#x2F;code&gt; method on the &lt;code&gt;Session&lt;&#x2F;code&gt; and returns either an ok when there are no more questions (&lt;code&gt;Ready&lt;&#x2F;code&gt;) or the question that is currently open.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-post-response&quot;&gt;&lt;code&gt;post_response()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Answer a question to the session under configuration.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Calls the &lt;code&gt;post_response()&lt;&#x2F;code&gt; on the currently &lt;code&gt;configuring&lt;&#x2F;code&gt; &lt;code&gt;Session&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-start&quot;&gt;&lt;code&gt;start()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Schedule the session under configuration with the provided arguments.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Returns an error if the &lt;code&gt;configuring&lt;&#x2F;code&gt; session has not signalled that it is ready yet or isn&#x27;t present.&lt;&#x2F;p&gt;
&lt;p&gt;Sets the sessions command to the given value, and swaps it into the &lt;code&gt;scheduled&lt;&#x2F;code&gt; stage, a session that already was in &lt;code&gt;scheduled&lt;&#x2F;code&gt; will be cancelled.&lt;&#x2F;p&gt;
&lt;p&gt;Sets a timer to fire the &lt;code&gt;SIGALRM&lt;&#x2F;code&gt; signal in five seconds which will be received using the &lt;code&gt;alarm()&lt;&#x2F;code&gt; method.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-alarm&quot;&gt;&lt;code&gt;alarm()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Gets called by the main-loop when a &lt;code&gt;SIGALRM&lt;&#x2F;code&gt; (timer) arrives.&lt;&#x2F;p&gt;
&lt;p&gt;Is a noop if no session is scheduled.&lt;&#x2F;p&gt;
&lt;p&gt;If the current session is still running it will be sent a &lt;code&gt;SIGTERM&lt;&#x2F;code&gt; or &lt;code&gt;SIGKILL&lt;&#x2F;code&gt;, depending on how much time has elapsed since the session was scheduled. The &lt;code&gt;SIGALRM&lt;&#x2F;code&gt; will be set up to fire again in one second.&lt;&#x2F;p&gt;
&lt;p&gt;If the current session is cleared the &lt;code&gt;seduled&lt;&#x2F;code&gt; session will be sent a &lt;code&gt;Start&lt;&#x2F;code&gt; command and be turned into the &lt;code&gt;current&lt;&#x2F;code&gt; session.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-context-methods-check-children&quot;&gt;&lt;code&gt;check_children()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Gets called by the main-loop when a &lt;code&gt;SIGCHLD&lt;&#x2F;code&gt; (a child process has terminated, usually) arrives.&lt;&#x2F;p&gt;
&lt;p&gt;This function calls the &lt;code&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;nix&#x2F;0.25.0&#x2F;nix&#x2F;sys&#x2F;wait&#x2F;fn.waitpid.html&quot;&gt;waitpid()&lt;&#x2F;a&gt;&lt;&#x2F;code&gt; function with the &lt;code&gt;NoHang&lt;&#x2F;code&gt; flag set to tell it that we don&#x27;t want to wait if there is nothing new it can tell us. It can react to a bunch of events:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In case of a &lt;code&gt;StillAlive&lt;&#x2F;code&gt; or an error indicating that we currently don&#x27;t have any children the function exits.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;States indicating some kind of normal event but not matching as well as Interruptions get thrown away.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Something exited or was killed with a signal and the &lt;code&gt;current&lt;&#x2F;code&gt; session owns that pid:&lt;&#x2F;p&gt;
&lt;p&gt;If a session is &lt;code&gt;scheduled&lt;&#x2F;code&gt; we try to start it and make it the &lt;code&gt;current&lt;&#x2F;code&gt; session, if not and the session was our greeter return an error, in case of a normal session use &lt;code&gt;start_greeter()&lt;&#x2F;code&gt; directly and make it the &lt;code&gt;current&lt;&#x2F;code&gt; session.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;greetd-context-methods-terminate&quot;&gt;&lt;code&gt;terminate()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Notify the Context that we want to terminate. This should be called on SIGTERM.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Shuts down the &lt;code&gt;configuring&lt;&#x2F;code&gt;, &lt;code&gt;scheduled&lt;&#x2F;code&gt; and &lt;code&gt;current&lt;&#x2F;code&gt; session while holding the write lock to avoid race conditions and sessions being rescheduled.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;state-machine&quot;&gt;State Machine&lt;&#x2F;h3&gt;
&lt;p&gt;All these functions form the state machine that cycles between greeter and user sessions and makes sure the pipeline from &lt;code&gt;configuring&lt;&#x2F;code&gt; to &lt;code&gt;current&lt;&#x2F;code&gt; doesn&#x27;t get clogged up by anything misbehaving and that greetd cleans up after itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;greetd-session-interface&quot;&gt;The Session Interface&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-code&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;git.sr.ht&amp;#x2F;~kennylevinsen&amp;#x2F;greetd&amp;#x2F;tree&amp;#x2F;3a32863bc70392449eb10c649cc2917a2ff06ad0&amp;#x2F;item&amp;#x2F;greetd&amp;#x2F;src&amp;#x2F;session&amp;#x2F;interface.rs&quot;&gt;
		The session interface is implemented in &lt;code&gt;greetd&#x2F;src&#x2F;session&#x2F;interface.rs&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;To make the external session processes and the context talk to each other Greetd has the &lt;code&gt;Session&lt;&#x2F;code&gt; class. It wraps an entire session process and handles the communication. It also remembers the last incoming message as &lt;code&gt;last_msg&lt;&#x2F;code&gt; and the session processes &lt;code&gt;pid&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;However, after setting up the session (as noted earlier) the Communication channel becomes useless as we now only care about the sessions current running status detecting when it exits and ending it if it misbehaves. This is the reason a second object, the &lt;code&gt;SessionChild&lt;&#x2F;code&gt; exists which holds the session managers pid as &lt;code&gt;task&lt;&#x2F;code&gt; and the pid of the process in the session as &lt;code&gt;sub_task&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greetd-session-methods&quot;&gt;Methods of &lt;code&gt;Session&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;greetd-session-methods-new-external&quot;&gt;&lt;code&gt;new_external()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Creates the &lt;code&gt;Session&lt;&#x2F;code&gt; object and spawns a session worker process with a datagram socket attached for communication.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-initiate&quot;&gt;&lt;code&gt;initiate()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Send an &lt;code&gt;InitiateLogin&lt;&#x2F;code&gt; message to the session worker.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-get-state&quot;&gt;&lt;code&gt;get_state()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Tries to receive a message from the session worker if there is no &lt;code&gt;last_msg&lt;&#x2F;code&gt; and stores it in &lt;code&gt;last_msg&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then it returns a &lt;code&gt;SessionState&lt;&#x2F;code&gt; struct contain either a question from PAM or a &lt;code&gt;Ready&lt;&#x2F;code&gt; if PAM has no further questions. This is used in the &lt;code&gt;Context.get_question()&lt;&#x2F;code&gt; method.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-cancel&quot;&gt;&lt;code&gt;cancel()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sends a &lt;code&gt;Cancel&lt;&#x2F;code&gt; message and resets &lt;code&gt;last_msg&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-post-response&quot;&gt;&lt;code&gt;post_response()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Send a response to an authentication question, or None to cancel the authentication attempt.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Sends a &lt;code&gt;PostAuthMessageResponse&lt;&#x2F;code&gt; message and resets &lt;code&gt;last_msg&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-send-args&quot;&gt;&lt;code&gt;send_args()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Send the arguments that will be used to start the session.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;commit&#x2F;bc7e369225b628f0fb4863c9cfc4f48a3d632d41&quot;&gt;Since 2022-08-13&lt;&#x2F;a&gt; this also sends a list of environment variables to match the new &lt;code&gt;Args&lt;&#x2F;code&gt; message.&lt;&#x2F;p&gt;
&lt;p&gt;Also reads a message after that and returns whether the command was successful.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-methods-start&quot;&gt;&lt;code&gt;start()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sends a &lt;code&gt;Start&lt;&#x2F;code&gt; request and waits either for an &lt;code&gt;Error&lt;&#x2F;code&gt; or a &lt;code&gt;FinalChildPid&lt;&#x2F;code&gt; answer while dismissing questions from PAM with empty answers.&lt;&#x2F;p&gt;
&lt;p&gt;After that it shouts down the socket and returns a &lt;code&gt;SessionChild&lt;&#x2F;code&gt; object as the &lt;code&gt;Session&lt;&#x2F;code&gt; object mainly made for session setup is no longer useful once the session is running.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;greetd-session-child-methods&quot;&gt;Methods of &lt;code&gt;SessionChild&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;greetd-session-child-methods-owns-pid&quot;&gt;&lt;code&gt;owns_pid()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Tests if the &lt;code&gt;task&lt;&#x2F;code&gt; equals the given process id. (Used for checking if the session terminated on a &lt;code&gt;SIGCHLD&lt;&#x2F;code&gt; signal in the &lt;code&gt;Context&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-child-methods-term&quot;&gt;&lt;code&gt;term()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Used to send the session process (&lt;code&gt;sub_task&lt;&#x2F;code&gt;) a &lt;code&gt;SIGTERM&lt;&#x2F;code&gt; to tell it that we want it shut down.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;greetd-session-child-methods-kill&quot;&gt;&lt;code&gt;kill()&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Sends a &lt;code&gt;SIGKILL&lt;&#x2F;code&gt; to both, the session manager and the session process. Used by the &lt;code&gt;Context.alert()&lt;&#x2F;code&gt; method when a session takes too long to shut down.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;updates-to-this-page&quot;&gt;Updates to this Page&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2022-09-11&quot;&gt;2022-09-11&lt;&#x2F;h3&gt;
&lt;p&gt;Added information about the commits adding back the environment variables and another round of spellchecking. May also have broken some links (I hope not too many).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2023-03-03&quot;&gt;2023-03-03&lt;&#x2F;h3&gt;
&lt;p&gt;Added Link to Kenny Levinsens blogpost.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;but-why&quot;&gt;But Why?&lt;&#x2F;h2&gt;
&lt;p&gt;The reason I created this writeup is to first of all have an understanding of what my login manager of choice does and also to have a reference when tinkering with it.
One of my plans with this includes making greetd capable of running multiple sessions simultaneously to allow user switching and to be able to reuse the greeter for the initial login for locking my screen.&lt;&#x2F;p&gt;
&lt;p&gt;In case you have found a mistake, have questions or just feedback in general …&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destime-contact&quot; href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;about&#x2F;me&#x2F;#contact&quot;&gt;
		… please contact me!
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Building a crappy cheap LightDM 1&#x2F;?</title>
        <published>2022-08-05T00:00:00+00:00</published>
        <updated>2022-08-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/cheap-lightdm-part-1/"/>
        <id>https://slatecave.net/blog/cheap-lightdm-part-1/</id>
        
        <summary type="text">First POC of a simple session switcher for building a feature-rich lockscreen</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/cheap-lightdm-part-1/">&lt;p&gt;This is a lightly edited mind dump, don&#x27;t expect a well written post here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-it-is-does-and-why-i-built-it&quot;&gt;What it is, does and why I built it&lt;&#x2F;h2&gt;
&lt;p&gt;The goal of this little project was to build a collection of tools that is able to switch between two sessions on a Linux system for the purpose of having a session for working and one for logging in and unlocking the screen, the way it is implemented is by changing &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Virtual_console&quot;&gt;virtual terminals&lt;&#x2F;a&gt; to the corresponding session when certain state-changes occur, much like &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;LightDM&quot;&gt;LightDM&lt;&#x2F;a&gt; and the light-locker.&lt;&#x2F;p&gt;
&lt;p&gt;The reason I&#x27;m doing this is because I want to be able to replace my currently pretty barebones screenlockers with a rich screenlocker that is able to do more than just showing me an unlock form, mostly for my phone but also for my other machines.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-not-just-use-lightdm&quot;&gt;Why not just use LightDM?&lt;&#x2F;h3&gt;
&lt;p&gt;Maybe oversimplified answer: I have the opinion that LightDM isn&#x27;t great on mobile.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;&#x2F;h2&gt;
&lt;p&gt;The core of this contraption are two instances of &lt;a href=&quot;https:&#x2F;&#x2F;sr.ht&#x2F;~kennylevinsen&#x2F;greetd&#x2F;&quot;&gt;greetd&lt;&#x2F;a&gt;, one for actually logging in and one for giving me a properly set up session to run my compositor in which could be replaced by an instance of tinydm on my phone. These two communicate by always running one of two scripts the locker and the unlocker, both grab the current vt with the &lt;code&gt;chvt&lt;&#x2F;code&gt; command, write to pidfiles and &lt;code&gt;kill&lt;&#x2F;code&gt; each other and run hooks on exit.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Please Note:&lt;&#x2F;strong&gt; For my current implementation the user that runs the logged in session needs passwordless sudo access to the &lt;code&gt;chvt&lt;&#x2F;code&gt; command, in a multiuser scenario this may not be the best idea.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;unlocking&quot;&gt;Unlocking&lt;&#x2F;h3&gt;
&lt;p&gt;Unlocking works by entering your credentials in the greeter, triggering the &lt;code&gt;unlock&lt;&#x2F;code&gt; script which sends a &lt;code&gt;SIGUSR1&lt;&#x2F;code&gt; to the locker process (using the pidfile), and then entering a sleep loop waiting for the next session lock.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;lock&lt;&#x2F;code&gt; script handles this &lt;code&gt;SIGUSR1&lt;&#x2F;code&gt; by killing the lockscreen (in my case swaylock) and using &lt;code&gt;chvt&lt;&#x2F;code&gt; to switch the currently active terminal to the one of the real-work-session.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;locking&quot;&gt;Locking&lt;&#x2F;h3&gt;
&lt;p&gt;Locking works almost exactly the reverse way as unlocking. The &lt;code&gt;lock&lt;&#x2F;code&gt; script starts the screenlocker, kills the unlocker, this time with an ordinary &lt;code&gt;SIGKILL&lt;&#x2F;code&gt; and starts waiting for being unlocked.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;unlocker&lt;&#x2F;code&gt; uses &lt;code&gt;chvt&lt;&#x2F;code&gt; to grab the vt with the greeter on it and exits telling greetd to start the greeter session for the next unlock.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bootstrapping&quot;&gt;Bootstrapping&lt;&#x2F;h3&gt;
&lt;p&gt;To solve our little problem of how to bootstrap this thing there is also an &lt;code&gt;initial-session-lock&lt;&#x2F;code&gt;, a script that writes its pid to the lockers pidfile and only exits with a success code when it receives a &lt;code&gt;SIGUSR1&lt;&#x2F;code&gt; to act as a trigger for starting the session on the first unlock. For shutting down my current solution is to wrap this in a loop and kill the unlocker after sway exits.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;code&quot;&gt;Code&lt;&#x2F;h2&gt;
&lt;p&gt;Currently this is an experiment I&#x27;ve run on my personal dotfiles so the sources are not really in any central location yet and you probably don&#x27;t want to just copypasta my scripts.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;scripts&quot;&gt;Scripts&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;commit&#x2F;c961b5dc3ecd35f887ecb512dcfa33799cecb947&#x2F;.local&#x2F;bin&#x2F;lock&quot;&gt;My personal screen-&lt;code&gt;lock&lt;&#x2F;code&gt; script&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;commit&#x2F;c961b5dc3ecd35f887ecb512dcfa33799cecb947&#x2F;.local&#x2F;bin&#x2F;unlocker&quot;&gt;My &lt;code&gt;unlocker&lt;&#x2F;code&gt; script&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dotfiles&#x2F;src&#x2F;commit&#x2F;c961b5dc3ecd35f887ecb512dcfa33799cecb947&#x2F;.local&#x2F;bin&#x2F;initial-session-lock&quot;&gt;My &lt;code&gt;initial-session-lock&lt;&#x2F;code&gt; script&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;greetd-configuration&quot;&gt;Greetd configuration&lt;&#x2F;h3&gt;
&lt;p&gt;As mentioned above I have two instances of greetd running, one that starts the unlocker on login and one that starts the actual session.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A pretty standard greetd configuration with the &lt;code&gt;unlocker&lt;&#x2F;code&gt; script instead of a real session.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;ini&quot; class=&quot;language-ini z-code&quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;z-source z-genconfig&quot;&gt;&lt;span class=&quot;z-storage z-type z-genconfig&quot;&gt;[terminal]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# The VT to run the greeter on. Can be &amp;quot;next&amp;quot;, &amp;quot;current&amp;quot; or a number
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# designating the VT.
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;vt&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-genconfig&quot;&gt;7&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# The default session, also known as the greeter.
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-genconfig&quot;&gt;[default_session]
&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# Starting tuigreet here which starts the unlocker command directly to unlock the second session
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;command&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-genconfig&quot;&gt;&amp;quot;tuigreet --cmd unlocker -r -t&amp;quot;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# The user to run the command as. The privileges this user must have depends
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# on the greeter. A graphical greeter may for example require the user to be
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# in the `video` group.
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;user&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-genconfig&quot;&gt;&amp;quot;_greeter&amp;quot;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;The one starting the actual session could be replaced by something like &lt;code&gt;tinydm&lt;&#x2F;code&gt; as I only care about the autostarting part and that the environment is properly set up so I don&#x27;t have to do it myself here.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;A greetd configuration that uses the `initial_session` mechanism to autostart a shell oneliner that starts our session.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;ini&quot; class=&quot;language-ini z-code&quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;z-source z-genconfig&quot;&gt;&lt;span class=&quot;z-storage z-type z-genconfig&quot;&gt;[terminal]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# The VT to run the greeter on. Can be &amp;quot;next&amp;quot;, &amp;quot;current&amp;quot; or a number
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# designating the VT.
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;vt&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-genconfig&quot;&gt;8&lt;&#x2F;span&gt;

&lt;span class=&quot;z-storage z-type z-genconfig&quot;&gt;[general]
&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# Don&amp;#39;t know if actually necessary but I don&amp;#39;t want to restart the session I&amp;#39;m writing this post in
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;runfile&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-genconfig&quot;&gt;&amp;quot;&#x2F;run&#x2F;slatian-greetd-initial-session&amp;quot;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-storage z-type z-genconfig&quot;&gt;[initial_session]
&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# The command that wits for the initial unlock, if successful starts the session and returns the control to the lockscreen after the session ends before it starts over.
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;command&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-genconfig&quot;&gt;&amp;quot;sh -c &amp;#39;while true; do initial-session-lock &amp;amp;&amp;amp; dbus-run-session sway; kill &lt;span class=&quot;z-constant z-character z-genconfig&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;$(head -n 1 &lt;span class=&quot;z-constant z-character z-genconfig&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;$XDG_RUNTIME_DIR&#x2F;unlocker.pid&lt;span class=&quot;z-constant z-character z-genconfig&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;)&lt;span class=&quot;z-constant z-character z-genconfig&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;; done&amp;#39;&amp;quot;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-param z-genconfig&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-genconfig&quot;&gt;user&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-genconfig&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-genconfig&quot;&gt;&amp;quot;slatian&amp;quot;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-meta z-comment z-genconfig&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-genconfig&quot;&gt;# default session omitted … nothing interesting there
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;sudo-configuration&quot;&gt;Sudo configuration&lt;&#x2F;h3&gt;
&lt;p&gt;The following snippet placed in a file in &lt;code&gt;&#x2F;etc&#x2F;sudoers.d&#x2F;&lt;&#x2F;code&gt; will give everyone in the &lt;code&gt;wheel&lt;&#x2F;code&gt; group (the group that has sudo access anyways, may also be called &lt;code&gt;sudo&lt;&#x2F;code&gt; on some systems) the permission to run &lt;code&gt;chvt&lt;&#x2F;code&gt; without having to enter a password.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;%wheel	ALL= NOPASSWD: &#x2F;usr&#x2F;bin&#x2F;chvt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;almost-eof&quot;&gt;Almost EOF …&lt;&#x2F;h2&gt;
&lt;p&gt;More or less obviously this is very hacky, has multiple issues and falls apart very easily, somewhere in the next few months I&#x27;ll try to find a cleaner way to implement this without relying on too much on shell scripting and firing signals at processes using pidfiles.&lt;&#x2F;p&gt;
&lt;p&gt;Until that happens I&#x27;ll continue to use &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;integrating-my-screenlocker-with-sxmo-part-2&#x2F;&quot;&gt;my sxmo screenlocker&lt;&#x2F;a&gt; on my phone because it mostly works well enough for what I need.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Screwing around with Sliderland</title>
        <published>2022-06-14T00:00:00+00:00</published>
        <updated>2022-09-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/sliderland-screw/"/>
        <id>https://slatecave.net/creations/sliderland-screw/</id>
        
        <summary type="text">Things I made in blinrys awesome Sliderland playground.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/sliderland-screw/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sliderland.blinry.org&#x2F;&quot;&gt;Sliderland&lt;&#x2F;a&gt; is a coding playground made by &lt;a href=&quot;https:&#x2F;&#x2F;blinry.org&#x2F;&quot;&gt;blinry&lt;&#x2F;a&gt; that evaluates some javascript and displays the result using 64 sliders (the original used 64 html sliders, but they were replaced in favour of a canvas that draws things that look like sliders because of browser compatibility). &lt;del datetime=&quot;2024-01-07&quot;&gt;I also host a Sliderland on sliderland.slatecave.net that has a dark mode and behaves more like a scrollable page.&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Some things I made with Sliderland follow, feel free to tweak them have fun and share them! (mentioning my name somewhere would be nice but isn&#x27;t required)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;screw-it-demo&quot;&gt;Screw it! Demo&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;sliderland.blinry.org&amp;#x2F;#%7B%0A%20%20%2F%2F%20sliders%0A%20%20var%20sliders%20%3D%2064%3B%0A%20%20%2F%2F%20speed%20%28negative%20values%20work%20too!%29%0A%20%20var%20z%20%3D%20t*3%3B%0A%20%20%2F%2F%20scale%20%280%20to%20.5%29%0A%20%20var%20scale%20%3D%20.2%3B%0A%20%20%2F%2F%20split%20%28how%20split%20up%20are%20two%20strands%20of%20waves%3F%29%0A%20%20var%20split%20%3D%200%3B%0A%20%20%2F%2F%20multiply%20%28make%20a%20screw%20of%20screws!%29%0A%20%20var%20multiply%20%3D%200%3B%0A%20%20%2F%2F%20number%20of%20edges%20%28waves%29%20%28looks%20less%20good%20for%20n%20%3E%205%29%0A%20%20var%20n%20%3D%200%3B%0A%20%20%2F%2F%20frequency%20%28when%20left%20at%200%20get%20set%20with%20some%20magic%20values%20that%20look%20pretty%29%0A%20%20var%20f%20%3D%200%0A%20%20%2F%2Ftransition%20clock%0A%20%20var%20tz%20%3D%20t%2F5%20%2B%20%28x*.2%29%3B%0A%20%20%2F%2F%20transition%20take%20how%20lmuch%20of%20a%20tick%3F%20%280%20to%201%29%0A%20%20var%20tp%20%3D%200.02%3B%0A%20%20%0A%20%20%2F%2F%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%205%7Csplit%0A%20%20var%20n_over_tz%20%3D%20%5B%200%2C%201%2C%202%2C%203%2C%204%2C%205%2C%201%2C%202%2C-2%2C-2%2C-2%2C%0A%20%20%2F%2F%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-2%2C-2%2C%203%2C%20-3%2C-3%2C%20-3%2C%204%2C-4%2C%20-4%2C%205%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-3%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%2C%200%5D%3B%0A%20%20%0A%20%20%2F%2F%20restart%20when%20information%20about%20how%20waves%20ends%0A%20%20var%20tz%20%3D%20tz%25n_over_tz.length%3B%0A%20%20%0A%20%20%2F%2F%20linear%20transform%20from%20time%2C%20to%20time%2C%20current%20time%2C%20value%20a%2C%20value%20b%0A%20%20function%20ltr%28f%2Ct%2Cc%2Ca%2Cb%29%20%7B%0A%20%20%20%20var%20m%20%3D%20%28c-f%29%2F%28t-f%29%3B%0A%20%20%20%20return%20%28a*%281-m%29%2Bb*m%29%0A%20%20%7D%0A%20%20%0A%20%20%0A%20%20%2F%2F%20turn%20animated%20transitions%20on%0A%20%20if%20%28true%29%20%7B%0A%20%20%20%20n%20%3D%20n_over_tz%5BMath.ceil%28tz%29%5D%3B%20%2F%2Fgoes%20until%20n%3D5%20then%20repeats%0A%20%20%20%20if%20%28n_over_tz%5BMath.round%28tz%2B1%29%5D%20%3E%200%29%20%7B%0A%20%20%20%20%20%20if%20%28tz%251%20%3C%20%28tp%2F2%29%20%7C%7C%20%28tz%251%29%20%3E%20%281-%28tp%2F2%29%29%29%20%7B%0A%20%20%20%20%20%20%20%20%2F%2Fcalculate%20animation%20progress%20%280%20to%201%29%0A%20%20%20%20%20%20%20%20at%20%3D%20%28%28tz%2B%28tp%2F2%29%29%251%29%2Ftp%3B%0A%20%20%20%20%20%20%20%20%2F%2Fshrink%20and%20come%20up%20with%20new%20wave%20count%0A%20%20%20%20%20%20%20%20scale%20%3D%20scale*%28cos%28PI*2*at%29*.5%2B.5%29%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28n%20%3C%200%29%20%7B%0A%20%20%20%20%20%20n%20%3D%20-n%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28tz%20%3E%205.2%20%26%26%20tz%20%3C%2011%29%20%7B%0A%20%20%20%20%20%20if%20%28tz%20%3C%205.7%29%20%7B%0A%20%20%20%20%20%20%20%20split%20%3D%20ltr%285.2%2C5.7%2Ctz%2C0%2C1%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20ltr%285.2%2C5.7%2Ctz%2C.2%2C.1%29%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%207.5%29%20%7B%0A%20%20%20%20%20%20%20%20split%20%3D%201%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.1%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2010%29%20%7B%0A%20%20%20%20%20%20%20%20split%20%3D%20cos%28ltr%287.5%2C10%2Ctz%2C0%2CPI*2%29*2%2Bx%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.1%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2010.5%29%20%7B%0A%20%20%20%20%20%20%20%20split%20%3D%20ltr%2810%2C10.5%2Ctz%2C1%2C0%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.1%3B%0A%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20scale%20%3D%20ltr%2810.5%2C11%2Ctz%2C.1%2C.2%29%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28tz%20%3E%2012%29%20%7B%0A%20%20%20%20%20%20if%20%28tz%20%3C%2013%29%20%7B%0A%20%20%20%20%20%20%20%20scale%20%3D%20ltr%2812%2C13%2Ctz%2C.2%2C.3%29%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2014%29%20%7B%0A%20%20%20%20%20%20%20%20multiply%20%3D%20ltr%2813%2C14%2Ctz%2C1%2C2%29*%28x%2B.5%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.3%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2019%29%20%7B%0A%20%20%20%20%20%20%20%20multiply%20%3D%202*%28x%2B.5%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20.3%3B%0A%20%20%20%20%20%20%20%20f%20%3D%201%3B%0A%20%20%20%20%20%20%7D%20else%20if%20%28tz%20%3C%2020%29%7B%0A%20%20%20%20%20%20%20%20multiply%20%3D%20ltr%2819%2C20%2Ctz%2C2%2C1%29%3B%0A%20%20%20%20%20%20%20%20scale%20%3D%20ltr%2819%2C20%2Ctz%2C.3%2C.2%29%3B%0A%20%20%20%20%20%20%20%20f%20%3D%201%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28tz%20%3E%2020%29%20%7B%0A%20%20%20%20%20%20n%20%3D%20ltr%2820%2C%2030%2C%20tz%2C%205%2C%200%29%3B%0A%20%20%20%20%20%20scale%20%3D%20ltr%2820%2C%2030%2C%20tz%2C%20.2%2C%200%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20if%20%28tz%20%3C%20.5%29%20%7B%0A%20%20%20%20%20%20n%20%3D%20ltr%280%2C%20.5%2C%20tz%2C%200.9%2C%201%29%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20%2F%2F%20frequency%20%28some%20magic%20values%20here%20that%20make%20it%20look%20okayish%29%0A%20%20if%20%28f%20%3D%3D%200%29%20%7B%0A%20%20%20%20f%20%3D%20%287*%283%2Fn%29%29%3B%0A%20%20%7D%0A%20%20%0A%20%20%2F%2F%20n%3A%20number%20of%20waves%0A%20%20%2F%2F%20f%3A%20frequency%0A%20%20%2F%2F%20t%3A%20current%20time%20%28maybe%20you%20want%20to%20manipulate%20it%29%0A%20%20%2F%2F%20max_i%3A%20how%20wide%20the%20field%20currently%20is%0A%20%20%2F%2F%20i%3A%20at%20which%20%22pixel%22%20we%20are%0A%20%20%2F%2F%20returns%3A%20a%20value%20between%20-1%20and%201%0A%20%20function%20screw%28n%2C%20f%2C%20t%2C%20max_i%2C%20i%29%20%7B%0A%20%20%20%20var%20x%20%3D%20i%2Fmax_i%3B%0A%20%20%20%20%2F%2F%20breakpoint%20when%20something%20goes%20into%20the%20background%0A%20%20%20%20var%20b%20%3D%201%3B%0A%20%20%20%20%2F%2Fugly%20hack%20%28maybe%20someone%20more%20skilled%20than%20me%20in%20math%20can%20figure%20out%20why%29%0A%20%20%20%20if%20%28n%20%3D%3D%203%29%20%7B%0A%20%20%20%20%20%20b%20%3D%20.865%3B%0A%20%20%20%20%7D%20if%20%28n%20%3E%3D%204%29%20%7B%0A%20%20%20%20%20%20b%20%3D%20Math.pow%282%2Fn%2C2%2FPI%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20var%20a%20%3D%200%3B%0A%20%20%20%20var%20am%20%3D%200%3B%0A%20%20%20%20%0A%20%20%20%20for%20%28var%20j%20%3D%200%3B%20j%20%3C%20n%3B%20j%2B%2B%29%20%7B%0A%20%20%20%20%20%20%2F%2Funcomment%20the%20break%20statement%20below%20when%20messing%20with%20the%20loop%0A%20%20%20%20%20%20%2F%2Fyour%20browser%20window%20will%20FREEZE%20otherwise!%0A%20%20%20%20%20%20%2F%2Fbreak%3B%0A%20%20%20%20%20%20%0A%20%20%20%20%20%20%2F%2F%20which%20wave%20are%20we%20currently%20on%3F%0A%20%20%20%20%20%20var%20m%20%3D%20%28%28i%2Bround%28n%2F2-%28n%252-1%29%29*j%29%25n%29%2Fn%3B%0A%20%20%20%20%20%20%2F%2F%20offset%20for%20this%20specific%20wave%0A%20%20%20%20%20%20var%20o%20%3D%20x*f%2B%28PI*2*m%29%3B%0A%20%20%20%20%20%20%2F%2F%20if%20the%20wave%20should%20be%20in%20the%20background%20don&amp;#x27;t%20set%20a%0A%20%20%20%20%20%20if%20%28cos%28o%2Bt%29%20%3E%20-b%29%20%7B%0A%20%20%20%20%20%20%20%20a%20%3D%20sin%28o%2Bt%29%3B%0A%20%20%20%20%20%20%20%20am%20%3D%20m%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20return%20%7By%3A%20a%2C%20m%3A%20am%7D%3B%0A%20%20%7D%0A%20%20%0A%20%20if%20%28split%20!%3D%200%29%20%7B%0A%20%20%20%20var%20abs_split%20%3D%20Math.ceil%28abs%28split%29%2B1%29%0A%20%20%20%20return%20screw%28n%2C%20f%2C%20z%2C%20sliders%2Fabs_split%2C%20Math.floor%28i%2Fabs_split%29%29.y*scale%2B.5%2B%28%28%28i%252%29-.5%29*.5*split%29%3B%0A%20%20%7D%20else%20if%20%28multiply%20%3E%201%29%20%7B%0A%20%20%20%20var%20abs_multiply%20%3D%20Math.ceil%28abs%28multiply%29%29%3B%0A%20%20%20%20var%20s%20%3D%20screw%28n%2C%20f%2C%20z%2C%20sliders%2C%20i%29%3B%0A%20%20%20%20return%20screw%28abs_multiply%2C%20f*10*abs_multiply%2C%20z*abs_multiply%2C%2064%2Fabs_multiply%2C%20i%2Fabs_multiply%29.y*scale*%28.2%2Fabs%28multiply%29%29%2B%28s.y*scale%2B.5%29%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20return%20screw%28n%2C%20f%2C%20z%2C%20sliders%2C%20i%29.y*scale%2B.5%3B%0A%20%20%7D%0A%0A%7D&quot;&gt;
		The &amp;quot;final&amp;quot; Screw demo that cycles through a few wavy things that look a bit like screws.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;sliderland.blinry.org&amp;#x2F;#%7B%0A%20%20%2F%2F%20speed%20%28negative%20values%20work%20too!%29%0A%20%20var%20z%20%3D%20t*2%3B%0A%20%20%2F%2F%20number%20of%20edges%20%28waves%29%20%28looks%20less%20good%20for%20n%20%3E%205%29%0A%20%20var%20n%20%3D%203%3B%0A%20%20%2F%2F%20wavelength%20%28some%20magic%20values%20here%20that%20make%20it%20look%20okayish%29%0A%20%20var%20l%20%3D%20%287*%283%2Fn%29%29%3B%0A%0A%20%20%2F%2F%20breakpoint%20when%20something%20goes%20into%20the%20background%0A%20%20var%20b%20%3D%20sqrt%282%2Fn%29%3B%0A%0A%20%20var%20a%20%3D%200%3B%0A%20%20%0A%20%20for%20%28var%20j%20%3D%200%3B%20j%20%3C%20n%3B%20j%2B%2B%29%20%7B%0A%20%20%20%20%2F%2Funcomment%20the%20break%20statement%20below%20when%20messing%20with%20the%20loop%0A%20%20%20%20%2F%2Fyour%20browser%20window%20will%20FREEZE%20otherwise!%0A%20%20%20%20%2F%2Fbreak%3B%0A%20%20%20%20%0A%20%20%20%20%2F%2F%20which%20wave%20are%20we%20currently%20on%3F%0A%20%20%20%20var%20m%20%3D%20%28%28i-j%29%25n%29%2Fn%3B%0A%20%20%20%20%2F%2F%20offset%20for%20this%20specific%20wave%0A%20%20%20%20var%20o%20%3D%20x*l%2B%28PI*2*m%29%3B%0A%20%20%20%20%2F%2F%20if%20the%20wave%20should%20be%20in%20the%20background%20don&amp;#x27;t%20set%20a%0A%20%20%20%20if%20%28cos%28o%2Bz%29%20%3E%20-b%29%20%7B%0A%20%20%20%20%20%20a%20%3D%20sin%28o%2Bz%29%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20return%20a*.2%2B.5%3B%0A%0A%7D&quot;&gt;
		A barebones screw that is a bit easier to understand.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;roofs-and-birds&quot;&gt;Roofs and Birds&lt;&#x2F;h2&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;sliderland.blinry.org&amp;#x2F;#%7B%0A%20%20%2F%2Fnumber%20of%20bars%20on%20screen%0A%20%20let%20bars%20%3D%204%0A%20%20%2F%2Foffset%20from%200%0A%20%20let%20off%20%3D%20t*.5%0A%20%20%0A%20%20%2F%2Fgenerate%20the%20bars%0A%20%20main_wave%20%3D%20sin%28%28-t%2Bfloor%28%28x%2Boff%29*bars%29%29*PI*.5%29%0A%20%20%0A%20%20%2F%2F%20a%20function%20that%20generates%20a%20triangle%20wave%20for%20the%20%22roofs%22%0A%20%20function%20triangle_wave%28x%29%20%7B%0A%20%20%20%20x%20%3D%20%28x%2FPI%29%252-1%0A%20%20%20%20if%20%28x%20%3C%20-.5%29%20%7B%0A%20%20%20%20%20%20return%20x*2%2B2%0A%20%20%20%20%7D%20else%20if%20%28x%20%3E%20.5%29%20%7B%0A%20%20%20%20%20%20return%20x*2-2%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20return%20-x*2%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20%2F%2Fgenerate%20a%20triangle%20wave%20that%20matches%20the%20moving%20bars%20and%20mix%20it%20with%20the%20bars%20to%20create%20a%20smooth%20effect%2C%20also%20scaled%20with%20the%20bars%20to%20point%20in%20the%20right%20direction%0A%20%20return%20%28abs%28triangle_wave%28%28%28x%2Boff%29*bars%29*PI%29%29*main_wave*.3%2Bmain_wave*.7%29*.5%2B.5%0A%7D%0A&quot;&gt;
		A scrolling wave of bars with roofs on them.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;sliderland.blinry.org&amp;#x2F;#%7B%0A%0A%20%20%2F%2F%20a%20function%20that%20generates%20a%20triangle%20wave%20for%20the%20%22roofs%22%0A%20%20function%20triangle_wave%28x%29%20%7B%0A%20%20%20%20x%20%3D%20abs%28%28x%2FPI%29%252%29-1%0A%20%20%20%20if%20%28x%20%3C%20-.5%29%20%7B%0A%20%20%20%20%20%20return%20x*2%2B2%0A%20%20%20%20%7D%20else%20if%20%28x%20%3E%20.5%29%20%7B%0A%20%20%20%20%20%20return%20x*2-2%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20return%20-x*2%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20%2F%2Fnumber%20of%20bars%20on%20screen%0A%20%20let%20bars%20%3D%205%0A%20%20%2F%2Foffset%20from%200%0A%20%20let%20off%20%3D%20%28-t*.5%29%0A%20%20%0A%20%20%2F%2Fgenerate%20the%20bars%0A%20%20main_wave%20%3D%20sin%28%28-t%2Bfloor%28%28x%2Boff%29*bars%29%29*PI*.8%29%0A%20%20%0A%20%20%2F%2F%20if%20the%20offset%20is%20negative%20we%20need%20a%20fifferent%20offset%20for%20the%20triangle%20wave%20to%20make%20it%20point%20in%20the%20right%20direction%0A%20%20let%20offset_correct%20%3D%200%0A%20%20if%20%28off%20%3C%200%29%20offset_correct%20%2B%3D%20.5%0A%0A%20%20%2F%2Fhow%20much%20of%20the%20bar%20is%20a%20roof%0A%20%20let%20roof_factor%3D.3%0A%20%20%2F%2F%20glitch%20effect%20every%20few%20seconds%0A%20%20if%20%28%28i%254%29%2F4%20%3C%20sin%28t*.5%29%29%20roof_factor%20%3D%20-roof_factor%20%0A%0A%20%20%2F%2Fgenerate%20a%20triangle%20wave%20that%20matches%20the%20moving%20bars%20and%20mix%20it%20with%20the%20bars%20to%20create%20a%20smooth%20effect%2C%20also%20scaled%20with%20the%20bars%20to%20point%20in%20the%20right%20direction%0A%20%20%2F%2F%20return%20triangle_wave%28-x*PI*4%29*.5%2B.5%0A%20%20return%20%28abs%28triangle_wave%28%28%28x%2Boff%29*bars%2B.5%29*PI-%28.5*PI%29%29%29*main_wave*roof_factor%2Bmain_wave*%281-abs%28roof_factor%29%29%29*.5%2B.5%0A%7D%0A&quot;&gt;
		Same demo as above with a glitch effect switching between bird and roof mode.
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Tutorial: Smart ssh jumping</title>
        <published>2022-06-05T00:00:00+00:00</published>
        <updated>2022-06-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/smart-ssh-jump/"/>
        <id>https://slatecave.net/blog/smart-ssh-jump/</id>
        
        <summary type="text">SSH jump hosts are great! However, having to think about the current network is a task better done by the CPU</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/smart-ssh-jump/">&lt;p&gt;I&#x27;m assuming you know what an ssh jump host is, why you want it and how you configure it in your ssh &lt;code&gt;ssh_config&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem-i-m-trying-to-solve&quot;&gt;The problem I&#x27;m trying to solve&lt;&#x2F;h2&gt;
&lt;p&gt;SSH jumping (via &lt;code&gt;JumpHost&lt;&#x2F;code&gt;) is usually useful when you have a bastion host you have to log into to reach the network behind. In some environments you never reach this &amp;quot;private&amp;quot; network in any other way, but if the private network belongs to your home- or office and the bastion is for connections from the big scary intertubes you want to use it only when necessary, meaning we need some kind of conditional &lt;code&gt;ProxyJump&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-scenario&quot;&gt;Example scenario&lt;&#x2F;h2&gt;
&lt;p&gt;Lets assume we have the following two hosts:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		tesla
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This is our bastion host publicly reachable
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		hawking
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This is our private host we actually want to connect to
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h2 id=&quot;the-easy-workaround&quot;&gt;The easy workaround&lt;&#x2F;h2&gt;
&lt;p&gt;The easy solution would be to have multiple aliases and using your brain to make the decision by using the hostname when at home and the hostname-remote when elsewhere.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example ssh configuration with different aliases for local and remote connections:&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Host tesla
	HostName ssh.example.org
	User nicola
	IdentityFile ~&#x2F;.ssh&#x2F;id_foo
	Port 22222

Host hawking
	User stephen
	IdentityFile […]

Host hawking-remote
	ProxyJump tesla
	User stephen
	IdentityFile […]
	HostName hawking
	Port 22
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;Problem is that you have to type the remote alias every time you&#x27;re not at home. Also using git this way is pretty annoying (you have to set up multiple remotes and manually tell git which one to use).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;smart-solution&quot;&gt;Smart solution&lt;&#x2F;h2&gt;
&lt;p&gt;The smart solution would be that ssh somehow could figure out if it is &amp;quot;at home&amp;quot; and if not enable the proxy jump option. For that we have to answer the following two questions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Are we at home?&lt;&#x2F;li&gt;
&lt;li&gt;How do we tell ssh to (not) use the ProxyJump?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;are-we-at-home&quot;&gt;Are we at home?&lt;&#x2F;h2&gt;
&lt;p&gt;To find out if we are at home there are several options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Querying DNS and for some address that is different in our home network - Too complicated to set up and is a &lt;abbr title=&quot;Pain In The Backside&quot;&gt;PITA&lt;&#x2F;abbr&gt; with slow resolvers (ssh connections already take 3 seconds to establish on school WiFi 🙄)&lt;&#x2F;li&gt;
&lt;li&gt;Comparing the WiFi name - This is not an option for wired networks.&lt;&#x2F;li&gt;
&lt;li&gt;Manually - forget it I wanted to get rid of that&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All of the above are not really desirable, so lets think of something else … One thing that is usually frowned upon is &lt;b&gt;fingerprinting&lt;&#x2F;b&gt;, however if we turn the concept around that your local machine fingerprints its environment all of the sudden it becomes a pretty good tool.&lt;&#x2F;p&gt;
&lt;p&gt;If the year is somewhere after 2020 you probably have IPv6 configured and if you are someone like me, you probably have your own local prefix which is a pretty unique identifier for a network which the router is nice enough to tell your device when it connects!&lt;&#x2F;p&gt;
&lt;p&gt;So the magic for finding out if you are at home becomes an &lt;code&gt;ip a&lt;&#x2F;code&gt; piped into a &lt;code&gt;grep -F &amp;quot;$ip_address&amp;quot;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For my purposes I wrote a little script called &lt;code&gt;has-ip-grep&lt;&#x2F;code&gt; that is a simple shell pipe returning success if a given &amp;quot;fingerprint&amp;quot; matches and fails if it does not.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;The &lt;code&gt;has-ip-grep&lt;&#x2F;code&gt; script which tests if the first argument given to it matches the IP-address output part of an &lt;code&gt;ip a&lt;&#x2F;code&gt; command.&lt;&#x2F;figcaption&gt;
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-set z-shell&quot;&gt;set&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt;-&lt;&#x2F;span&gt;e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;ip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; a&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;awk&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-single z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&#x2F;inet&#x2F; { print $2 }&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-pipe z-shell&quot;&gt;|&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;grep&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;F&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;p&gt;Explanation: &lt;code&gt;ip a&lt;&#x2F;code&gt; prefixes the addresses assigned to interfaces with &lt;code&gt;inet&lt;&#x2F;code&gt; or &lt;code&gt;inet6&lt;&#x2F;code&gt; the awk command therefore looks for lines containing the string &lt;code&gt;inet&lt;&#x2F;code&gt; and if it finds one outputs the second column which is the IP-address. The output gets piped into a silent grep command that is told to look for instances of the first script argument if that&#x27;s not the case. The &lt;code&gt;set -e&lt;&#x2F;code&gt; makes sure the script returns an error code if that match fails.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;&#x2F;h3&gt;
&lt;p&gt;In the first example we try to match against our local IPv4 loopback interface which should always return a success, which works great for a quick test!&lt;&#x2F;p&gt;
&lt;figure&gt;
	
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;has-ip-grep&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; 127.0.0&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	&lt;figcaption&gt;&lt;b&gt;Example 1:&lt;&#x2F;b&gt; Succeeds because we have the IPv4-loopback-address assigned to our &lt;code&gt;lo&lt;&#x2F;code&gt; interface.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The second example show matching against the prefix usually used by a popular series of home routers made by a German company, this is also great for testing but it will very likely not be enough to identify your home network because your neighbours and your friend&#x27;s network probably have the same fingerprint.&lt;&#x2F;p&gt;
&lt;figure&gt;
	
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;has-ip-grep&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; 192.168.178&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	&lt;figcaption&gt;&lt;b&gt;Example 2:&lt;&#x2F;b&gt; Usually succeeds if you are connected to a popular series of German home routers.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Other problems are that because we match substrings here this will only be convenient to match against ranges that align nicely with the decimal notation and that we get false positives, for example if someone assigns us a &lt;code&gt;10.192.168.178&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We can reduce the probability of false positives by matching against our IPv6-address, replace the &lt;code&gt;fd45:6789:abcd:&lt;&#x2F;code&gt; in the third Example with your own local IPv6-prefix. I don&#x27;t recommend you rely your global unicast prefix as it is usually assigned by your &lt;abbr title=&quot;Internet Service Provider&quot;&gt;ISP&lt;&#x2F;abbr&gt; and may change behind your back.&lt;&#x2F;p&gt;
&lt;figure&gt;
	
	&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;has-ip-grep&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; fd45:6789:abcd:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	&lt;figcaption&gt;&lt;b&gt;Example 3:&lt;&#x2F;b&gt; Will succeed in your home network and maybe some other random network half around the globe you will never know exists.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;So now we have a pretty good (works most of the time) way to know if we are at home lets put it to use!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conditional-proxyjump&quot;&gt;Conditional ProxyJump&lt;&#x2F;h2&gt;
&lt;p&gt;Luckily ssh has exactly what we need to combine it with our command we just created.&lt;&#x2F;p&gt;
&lt;p&gt;You are probably familiar with the &lt;code&gt;Host&lt;&#x2F;code&gt; keyword in your ssh configuration, turns out if you have a decently modern version of ssh there is also a &lt;code&gt;Match&lt;&#x2F;code&gt; keyword which supports some arguments.&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-destination-text&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;man.voidlinux.org&amp;#x2F;ssh_config#Match&quot;&gt;
		See the manual: &lt;code&gt;man 5 ssh_config&lt;&#x2F;code&gt;
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;With that knowledge you can match against the target host (if you don&#x27;t you&#x27;ll run into funny recursion …), the network fingerprint of your choice and arrive at a configuration that can decide on the proxy without having to query your brain.&lt;&#x2F;p&gt;
&lt;figure&gt;
	&lt;figcaption&gt;Example ssh configuration which automagically knows how to connect.&lt;&#x2F;figcaption&gt;
	&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Host tesla
	HostName ssh.example.org
	User nicola
	IdentityFile ~&#x2F;.ssh&#x2F;id_foo
	Port 22222

Host hawking
	User stephen
	IdentityFile […]

Match Host hawking !Exec &amp;quot;has-ip-grep &amp;#39;192.168.42&amp;#39; &amp;amp;&amp;amp; has-ip-grep &amp;#39;fd42:f00b:aba2:&amp;#39;&amp;quot;
	ProxyJump tesla
	HostName hawking
	Port 22
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

	
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;explanation&quot;&gt;Explanation&lt;&#x2F;h3&gt;
&lt;p&gt;The configuration for tesla stays the same (assuming we only want to connect to it from the public side). The configuration for the local connection to hawking can stay too, the &lt;code&gt;Host hawking-remote&lt;&#x2F;code&gt; directive was replaced with a &lt;code&gt;Match&lt;&#x2F;code&gt; that tests if we want to connect to hawking and then executes our shell command.&lt;&#x2F;p&gt;
&lt;p&gt;In this case we check for two IP-addresses that we expect to get assigned in our private network, As I&#x27;m running dual stack it won&#x27;t hurt to check against the IPv4-address too.&lt;&#x2F;p&gt;
&lt;p&gt;These two commands are combined with some shell syntax and quoted using double-quotes because that&#x27;s how &lt;code&gt;ssh_config&lt;&#x2F;code&gt; works. The Exec is prefixed with an exclamation mark because we want to match if we are NOT at home. The part after that is the same as with the remote alias except for the User and IdentityFile because we now the &lt;code&gt;Host hawking&lt;&#x2F;code&gt; always matches and we don&#x27;t have to duplicate them anymore.&lt;&#x2F;p&gt;
&lt;p&gt;I can imagine it being a lot of fun configuring several Networks this way 🙃.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;security&quot;&gt;Security&lt;&#x2F;h2&gt;
&lt;p&gt;This is by no means any kind of security measure, if the network you are connected to matches the fingerprint of your private network this will make the wrong decision to connect directly! However if you are running such a setup you should have the certificate fingerprints of your real servers already in your known_hosts file and then just be smart enough to not accept any new certificates over untrusted connections.&lt;&#x2F;p&gt;
&lt;p&gt;If that finger print matches when you are not in your network (assuming you randomly chose an IPv6-prefix) you should get the hell out of there!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;debugging&quot;&gt;Debugging&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;ssh -v&lt;&#x2F;code&gt; is your friend!&lt;&#x2F;p&gt;
&lt;p&gt;If ssh seems to do nothing except burning CPU cycles you might have ran into some recursion problem, Make sure that your &lt;code&gt;Match&lt;&#x2F;code&gt; keywords always match on some host and that your proxy connections aren&#x27;t jumping in circles!&lt;&#x2F;p&gt;
&lt;p&gt;Also some pits I fell into (all of them are documented in the &lt;code&gt;ssh_config&lt;&#x2F;code&gt; man-page):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If your forget the &lt;code&gt;Host&lt;&#x2F;code&gt; rule on a &lt;code&gt;Match&lt;&#x2F;code&gt; you&#x27;ll get some recursion going.&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;code&gt;Host&lt;&#x2F;code&gt; rule ends when a &lt;code&gt;Match&lt;&#x2F;code&gt; rule starts.&lt;&#x2F;li&gt;
&lt;li&gt;When you assign a variable multiple times only the value from the first assignment is taken (especially important when you want to rewrite the host for short aliases and later to localhost when redirecting into a tunnel which won&#x27;t work).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;that-s-it&quot;&gt;That&#x27;s it!&lt;&#x2F;h2&gt;
&lt;p&gt;Have fun connecting to your servers without having to think about changing names! If you spotted a mistake somewhere or have other feedback feel free to contact me!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Integrating swaylock-mobile with Sxmo 2&#x2F;?</title>
        <published>2022-05-07T00:00:00+00:00</published>
        <updated>2022-05-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-2/"/>
        <id>https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-2/</id>
        
        <summary type="text">First attempt to itegrate my screenlcker with sxmo (quirky and barebones, but as success)</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-2/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;lists.sr.ht&#x2F;~mil&#x2F;sxmo-announce&#x2F;%3C3FV34P1BUD8B6.34I97X4SKLKHH%40yellow-orcess.my.domain%3E&quot;&gt;Sxmo 1.9.0 was released last weekend&lt;&#x2F;a&gt; for PostmarketOS edge, which means … I have continued my work on the screenlocker as promised. In this part I&#x27;ll mostly explain what needed to be done (and why) for the screenlocker to work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;related-resources&quot;&gt;Related Resources&lt;&#x2F;h2&gt;

	

&lt;ul class=&quot;link-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;integrating-my-screenlocker-with-sxmo-part-1&#x2F;&quot;&gt;Part 1 documenting how the screenlocking part of sxmo works under the hood&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;experiment.sxmo-screelocker&quot;&gt;Screenlocker Integration Dotfiles&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;swaylock-mobile&quot;&gt;Swaylock mobile, my screenlocker&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

&lt;h2 id=&quot;what-my-current-solution-can-and-can-t-do&quot;&gt;What my current solution can and can&#x27;t do&lt;&#x2F;h2&gt;
&lt;p&gt;Current sate of swaylock-mobile is, that it&#x27;s a pretty standard swaylock that fits on a touchscreen (even when rotated!) with a keypad for entering numeric pins without a real keyboard, that is enough to integrate it into Sxmo and get some additional security for my phone in daily use.&lt;&#x2F;p&gt;
&lt;p&gt;So the integration currently handles:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Locking the screen, before turning it off&lt;&#x2F;li&gt;
&lt;li&gt;Proximity-lock for both locked and unlocked states&lt;&#x2F;li&gt;
&lt;li&gt;State-management for the idle locker and lisgd&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;More important, what does it NOT handle:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;X integration, I have not tested it with the dwm version&lt;&#x2F;li&gt;
&lt;li&gt;Graceful degradation, fallback in case there is no screenlocker doesn&#x27;t work&lt;&#x2F;li&gt;
&lt;li&gt;Maybe I have broken some suspend inhibitors … I did&lt;&#x2F;li&gt;
&lt;li&gt;Remaining slightly broken states&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;changes&quot;&gt;Changes&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;new-wrapper-script-run-screenlocker&quot;&gt;New Wrapper script run-screenlocker&lt;&#x2F;h3&gt;
&lt;p&gt;This is a script that runs swaylock-mobile until it exits with a success status-code (the warning that you should make sure it is restarted in case of a crash still applies) and after that calls the unlock hook. This script gets called from the lock hook and should be the only thing that can call the unlock hook. (one exception would be the fallback …)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hooks-that-were-modified&quot;&gt;Hooks that were modified&lt;&#x2F;h3&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		sxmo_hook_inputhandler.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The Powerbutton now only toggles screenon and screenoff and calls the lock hook before off, also the corner-swipe locks the screen and turns it off.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_lock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Just does, what it says on the tin: locking, also makes sure it only locks when called from an unlocked sate, also invokes the new idlelocker hook.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_proximitylock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		The proximitylock now calls the screenon hook instead of the unlock hook, also disabled the can_suspend locks.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_screenoff.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Now uses $SXMO_STATE.screen for storing screen state.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_unlock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Now uses the idlelocker hook for setting the screenlocker.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;hooks-that-were-added&quot;&gt;Hooks that were added&lt;&#x2F;h3&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		sxmo_hook_idlelocker.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Sets the idlelocker for screenon scenarios, depending on the SXMO_STATE.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		sxmo_hook_screenon.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Gets called for turning the screen on (and some screen related services).
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;h3 id=&quot;new-state-file&quot;&gt;New state-file&lt;&#x2F;h3&gt;
&lt;p&gt;There is a new file to keep track of things, previously $SXMO_STATE was used for storing three states sxmo could be in: screenoff, locked and unlocked.&lt;&#x2F;p&gt;
&lt;p&gt;These states would still be sufficient, but the model with a state for the lock (locked and unlocked) and a state for the screen (on and off), which allows me to simplify the previously error prone and complex state machine into two state-machines which just turn things on and off without having to make assumptions about the screen-state or the lock status.&lt;&#x2F;p&gt;
&lt;p&gt;For that I needed a second state-file though which I called $SXMO_STATE.screen for now (clean solution would be to introduce a $SXMO_SCREEN_STATE and $SXMO_LOCK_STATE).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;new-environment-variable-sxmo-reason&quot;&gt;New environment variable: SXMO_REASON&lt;&#x2F;h3&gt;
&lt;p&gt;Before rewriting the state-machines, I tried to work with the old states and figuring out what to do depending on where the call came from, which turned out to be a horrible buggy mess, but its the reason SXMO_REASON was introduced.&lt;&#x2F;p&gt;
&lt;p&gt;Why is it still there you ask: Because if the screen is locked and the proximity-lock triggers in most of the cases you don&#x27;t want the screen to turn on again by just uncovering the proximity sensor… the screenon hook script currently uses it to cancel if this variable is set to proximity-lock and the lockscreen is active. (Someone could make that a preference, however it won&#x27;t work when the system goes to sleep)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;additional-sway-configuration&quot;&gt;Additional sway configuration&lt;&#x2F;h3&gt;
&lt;p&gt;One thing that isn&#x27;t immediately obvious is that you want to turn your screen back on again and that it doesn&#x27;t work with the default configuration, the reason being that sway detects when a proper screenlocker is active it only allows keyboard bindings (the power-button behaves as if it was the XF86PowerOff key on a keyboard) when they are bound with a --locked switch, so I put a bindsym into my sway config file, the cleaner way would have been to update sxmo_swayinitconf.sh (I did it that way because I hope this way it will break less often than modifying the swayinitconf script).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fixing-the-remaining-todos&quot;&gt;Fixing the remaining TODOs&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;it-doesn-t-work-with-x&quot;&gt;It doesn&#x27;t work with X&lt;&#x2F;h3&gt;
&lt;p&gt;I (or someone who knows sxmo) would have to make sure all modifications are also applied to the dwm version (not sure about how to solve the power-button problem though)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;graceful-degradation-in-case-of-missing-disabled-screenlocker&quot;&gt;Graceful degradation in case of missing&#x2F;disabled screenlocker&lt;&#x2F;h3&gt;
&lt;p&gt;My approach would be a dummy screenlocker, that creates a file somewhere and then watches it for close_writes with inotifywait (and deletes it after), the handler for the power-button in the file exists + locked + screen on would touch that file, which would trigger an unlock.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;broken-suspend-inhibitors&quot;&gt;Broken suspend inhibitors&lt;&#x2F;h3&gt;
&lt;p&gt;The proximity-lock hook by default locks the can_suspend mutex, I leave it on for the lock-screen which blocks the suspend script from doing its job, that&#x27;s not battery friendly … Lets just bypass that by disabling the lock and hope it won&#x27;t break.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;slightly-broken-states&quot;&gt;Slightly broken states&lt;&#x2F;h3&gt;
&lt;p&gt;There are two scenarios I know of where the state handling is slightly broken, both involve the proximity-lock:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When unlocked + proximity-lock active (sensor covered) + power button the screen turns on for a second and then goes to lock + screenoff. I currently don&#x27;t know why exactly this happens as the power-button should check against the screen-state and toggle it once, turning it on …&lt;&#x2F;li&gt;
&lt;li&gt;When locked + proximity-lock active + power button the screen turns on (like I want it when unlocked, ironically) and stays on for the 8 seconds timeout when not touched, this can be explained by how the proximity-lock currently triggers on changes (which is very good, because it saves power!)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Integrating swaylock-mobile with Sxmo 1&#x2F;?</title>
        <published>2022-03-18T00:00:00+00:00</published>
        <updated>2022-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-1/"/>
        <id>https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-1/</id>
        
        <summary type="text">Finding out how sxmo curerently handles screenlocking and suspending (pre sxmo 1.9.0)</summary>
        
        <content type="html" xml:base="https://slatecave.net/blog/integrating-my-screenlocker-with-sxmo-part-1/">&lt;p&gt;I have been trying to introduce my touchscreen based screenlocker to Sxmo for over a week now. Turns out it is not as easy as I thought.
In this part of the sersies I&#x27;ll try to describe what sxmo does for screenlocking and what changes with the 1.9.0 release. The actual implementation will be discussed in part 2.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-have-tried-so-far&quot;&gt;What I have tried so far&lt;&#x2F;h2&gt;
&lt;p&gt;My first approach was to try out the desktop screensaver, the problem was, that it tried to interact with the newer version of Sxmo, so I reverted to messing with the default screenlock hook.&lt;&#x2F;p&gt;
&lt;p&gt;This at first seemed simple, just insert the screenlocker after the service starting and stopping foo, lock my phone, the screenlocker comes up as expected, I try to enter my pin and … nothing, I forgot that Sxmo turns off the touch input for the locked mode, so I&#x27;m not able to enter my pin on the touchscreen. Ok easy fix turn the touch back on after starting the screenlocker … and yes I&#x27;m successfully able to unlock my phone, but for some reason the screenlocker comes up very soon and after coming Up my phone won&#x27;t go to sleep …&lt;&#x2F;p&gt;
&lt;p&gt;The reason turns out to be that the sxmo_screenlock.sh script figures out the current state by looking at what it turned off. That means, I broke that by messing with the touchscreen state, which brings us to the present.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-i-m-now&quot;&gt;Where I&#x27;m now&lt;&#x2F;h2&gt;
&lt;p&gt;This is the &amp;quot;legacy&amp;quot; script that still runs on my phone which will be removed when Sxmo updates to 1.9.0. See the next section for the new solution.&lt;&#x2F;p&gt;
&lt;p&gt;This script seems to be the pint where everything related to locking and sleeping goes through.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-state&quot;&gt;Setting state&lt;&#x2F;h3&gt;
&lt;p&gt;It defines the following functions for setting state:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		lock
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Turns touch input off
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		unlock
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Turns both touch input and the screen back on
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		off
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Turns the screen off
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		crust
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Sends the phone to deep sleep and determines why it woke up
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;I probably want to to add some semantics here to find out why the locking&#x2F;screenoff happened and launch my screenlocker if the reason is an idle session or a power button event.&lt;&#x2F;p&gt;
&lt;p&gt;These functions get called from a few places:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
			
				
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;&quot;&gt;

	
		&lt;dt&gt;
		core&#x2F;sxmo_inputhandler.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Has a lock_screen_action() function for when the screen is locked to tun it off, into locked and back into unlocked.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dd&gt;
		Also calls the lock function when you swipe away from the bottom left corner.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		core&#x2F;sxmo_screenlock_deeper.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Similar to the the lock_screen_action() above but is intended for being called after a few seconds of idle time and it won&#x27;t move to an unlocked state.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		core&#x2F;sxmo_proximitylock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This script periodically checks if the distance sensor is covered and then makes the decision to call the unlock or the off function, also based on the state of the screenlocker.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		core&#x2F;sxmo_rtcwake.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		This script seems to be called by nothing … it calls the crust function on sxmo_screenlock.sh
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		default_hooks&#x2F;contextmenu
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls lock, off and crust with the intention of explicitly locking the screen.
		&lt;&#x2F;dd&gt;
	

	

	
		&lt;dt&gt;
		notifications&#x2F;sxmo_notificationwrite.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Calls the lock function if the screen is off, probably with the intention to make the screen light up when there is a notification.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Idea from wiring that: start the screenlocker as a sxmo_service from a blocking-hook and make the unlock function refuse to work as long as it is running, execute the unlock when it returns, if no lockscreen hook is specified, fallback to turning off the touchscreen.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;querying-state&quot;&gt;Querying state&lt;&#x2F;h3&gt;
&lt;p&gt;These states from above can also be queried with the getCurState function, it returns:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		unlock
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When unlocked, this is returned if the touchscreen is on
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		lock
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When locked with the screen on, this is returned if touch is off but the screen is on
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		off
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When locked with screen off, this is returned when both touch and screen are off
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;This will need some modifications as I&#x27;m breaking the assumption that touch state equals locked state.&lt;&#x2F;p&gt;
&lt;p&gt;Quite a few scripts rely on this one:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		notifications&#x2F;sxmo_notificationwrite.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Tests for the off state to turn he screen on to get attention.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_inputhandler.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Uses this to cycle states.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_screenlock_deeper.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Uses this to determine the next deeper lock&#x2F;sleep state.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_proximitylock.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		(Ab)uses this to avoid unnecessary calls to unlock and off. But is not really context aware. It probably gets stopped elsewhere.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_rtcwake.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Cronjob, Only goes to crust if not in an unlocked state. (It has other factor too, but not being unlocked is a requirement)
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;Looking at this I&#x27;m not really willing to break this function, maybe some kind of soft&#x2F;hard lock mechanism for just turning off touch and having a proper lockscreen up would be useful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-new-screenlock-suspend-mechanism&quot;&gt;The new screenlock &#x2F; suspend mechanism&lt;&#x2F;h2&gt;
&lt;p&gt;To understand the new mechanism you first have to understand sxmo_mutex.sh. (&lt;a href=&quot;https:&#x2F;&#x2F;git.sr.ht&#x2F;~mil&#x2F;sxmo-utils&#x2F;commit&#x2F;ec6d08e91b94fc725733f65c7dbf313feb2a35cd#scripts&#x2F;core&#x2F;sxmo_mutex.sh&quot;&gt;It is documented in it&#x27;s first commit.&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;Diff from previous, old mechanism:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The is_idle and can_suspend hooks were replaced by mutex locks called from various sources.&lt;&#x2F;li&gt;
&lt;li&gt;The crust part of sxmo_screenlock.sh was replaced by sxmo_suspend.sh and sxmo_hook_suspend.sh.&lt;&#x2F;li&gt;
&lt;li&gt;The hacky detect locked state approach was moved to a much cleaner have a file which holds the state approach.&lt;&#x2F;li&gt;
&lt;li&gt;The rest of sxmo_screenlock.sh was replaces by hook scripts which makes my journey much easier.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;suspending&quot;&gt;Suspending&lt;&#x2F;h3&gt;
&lt;p&gt;This suspend part consists of a few files:&lt;&#x2F;p&gt;




	
		
			
		
	
		
			
			
				
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	
		
			
		
	
		
			
			
		
	


&lt;dl class=&quot;max-one-dd&quot;&gt;

	
		&lt;dt&gt;
		default_hooks&#x2F;sxmo_hook_suspend.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Does the actual suspending.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		default_hooks&#x2F;sxmo_hook_mnc.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Figures out how long it is until the next Chronjob with sxmo_rtcwake in its name is scheduled.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		default_hooks&#x2F;sxmo_hook_presuspend.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Called before suspending, does cleanup like pausing media and turning off screen.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		default_hooks&#x2F;sxmo_hook_postwake.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Called after waking up with the reason for the wakeup as the first argument.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		core&#x2F;sxmo_suspend.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		When called fires off the whole suspend hook firework, goes too sleep, wakes up again and does a bit of housekeeping.
		&lt;&#x2F;dd&gt;
	

	
		&lt;dt&gt;
		ore&#x2F;sxmo_rtcwake.sh
		&lt;&#x2F;dt&gt;
	

	
		&lt;dd&gt;
		Cronjob wrapper (I assume) that does some housekeeping with locks to make sure the cronjobs don&#x27;t get interrupted.
		&lt;&#x2F;dd&gt;
	

&lt;&#x2F;dl&gt;
&lt;p&gt;The whole thing is triggered from the screenoff hook which starts a sxmo_periodically which runs sxmo_hook_check_state_mutexes.sh and if there is no good reason to stay awake goes to sleep using sxmo_suspend.sh with a sxmo_mutex.sh and a holdexec. (At least for touchscreens with three buttons which is the category my pinephone falls into)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;locking&quot;&gt;Locking&lt;&#x2F;h3&gt;
&lt;p&gt;Sxmo still has the three states, but they were moved to their own hooks.&lt;&#x2F;p&gt;
&lt;p&gt;This means that …&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The unlock script is now responsible for calling sxmo_hook_lock.sh&lt;&#x2F;li&gt;
&lt;li&gt;The lock script is responsible for turning the screen off&lt;&#x2F;li&gt;
&lt;li&gt;The screenoff script is responsible for going to deep sleep&lt;&#x2F;li&gt;
&lt;li&gt;Unlocking still happens through the lockstate cycling in the inputhandler&lt;&#x2F;li&gt;
&lt;li&gt;The unlock script is called by the start hook&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The proximitylock script calls the screenoff hook to lock and the unlock hook, so a screenlocker can actually be placed in the lock hook without having to worry about the proximitylock interfering, the postwake hook would have to unlock the touchscreen from the screenoff hook, which it already does and the lock hook shouldn&#x27;t turn off the touchscreen (that&#x27;s a beautiful hook fallback), the only &amp;quot;problem&amp;quot; with this approach would be that the state would say unlocked while the lockscreen is up. (interactive would be a better identifier)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-be-continued&quot;&gt;To be continued&lt;&#x2F;h2&gt;
&lt;p&gt;Note 2022-05-07: there was a previous version of this article that said: this little diff will do it, just use the lock hook … Nope, that won&#x27;t work …&lt;&#x2F;p&gt;
&lt;p&gt;Most of the above is still true for the released version (good enough as a little guide for myself at least).&lt;&#x2F;p&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;integrating-my-screenlocker-with-sxmo-part-2&#x2F;&quot;&gt;
		For the actual implementation see part 2
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Swaylock Mobile</title>
        <published>2022-03-03T00:00:00+00:00</published>
        <updated>2023-05-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/swaylock-mobile/"/>
        <id>https://slatecave.net/creations/swaylock-mobile/</id>
        
        <summary type="text">Sxmo with sway is great but unfortunately it lacks a proper lockscreen, This is my attempt to fix that situation by adding a keypad and some mobile related cosmetic features to swaylock.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/swaylock-mobile/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;So far I have implemented:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A flat typing indicator that is (in my opinion) a better fit for small screens&lt;&#x2F;li&gt;
&lt;li&gt;A numeric keypad at the bottom, currently only reacting to touch input that is not very configurable&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Also this is my first wayland project and my first attempt at writing more than a &amp;quot;Hello World&amp;quot; in C.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;log&quot;&gt;Log&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2022-03-06-initial-release&quot;&gt;2022-03-06 Initial release&lt;&#x2F;h3&gt;
&lt;p&gt;The first goal was to get a working prototype that looks acceptable (Some alignments are still off). I have reached this by implementing a flat indicator that works a bit like the ring but is a line with the width configured using the radius and if necessary with a text-box below.&lt;&#x2F;p&gt;
&lt;p&gt;I was able to lock my phone by manually calling it and unlock it using the new keypad, next step is to integrate it with Sxmo so that I finally don&#x27;t have to rely on the incompetence of a potential attacker for the security of my phone.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2022-03-08-schlock&quot;&gt;2022-03-08 schlock&lt;&#x2F;h3&gt;
&lt;p&gt;Apparently I&#x27;m not the first to attempt to write a lockscreen with numeric keypad based on swaylock, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;telent&#x2F;schlock&quot;&gt;schlock&lt;&#x2F;a&gt;, written by &lt;a href=&quot;https:&#x2F;&#x2F;ww.telent.net&#x2F;&quot;&gt;@telent_net&lt;&#x2F;a&gt; appeared about two weeks ago. (Thank you for the link goes to &lt;a href=&quot;https:&#x2F;&#x2F;www.willowbarraco.fr&#x2F;&quot;&gt;Willow Barraco&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;It also is targeted at the touchscreen + numeric pin usecase but our approaches under the hood seem to be completely different.&lt;&#x2F;p&gt;
&lt;p&gt;While the overall approach with schlock is to replace the UI of swaylock with a virtual numpad and a custom indicator (see schlock&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;telent&#x2F;schlock&#x2F;blob&#x2F;main&#x2F;render.c&quot;&gt;render.c&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;telent&#x2F;schlock&#x2F;blob&#x2F;main&#x2F;pinentry.c&quot;&gt;pinentry.c&lt;&#x2F;a&gt;) the my swaylock-mobile trys to adapt and expand the UI to include touchscreen functionality so that my fork remains useful on non-touchscreens. However, the outcome is mostly the same for touchscreens.&lt;&#x2F;p&gt;
&lt;p&gt;On the password matching side schlock is a bit more creative than swaylock-mobile, it seems to (I&#x27;m not sure, judging based on having looked at the commit diffs for about half an hour) have a separate code-path that matches against the hash of a pin in a file (that could be a backend and it would be awesome!) while mine works like the phosh lockscreen and simply asks pam (or whatever your swaylock backend is) if the entered pin is the users password (ab)using the existing mechanism for keyboards.&lt;&#x2F;p&gt;
&lt;p&gt;There is no better here, just ideas from two screenlockers aimed at a phone that runs linux with their own ideas on how to get from a common starting point to a common goal.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2022-03-19-integrating-with-sxmo-1-9-0&quot;&gt;2022-03-19 Integrating with sxmo 1.9.0&lt;&#x2F;h3&gt;
&lt;p&gt;I wrote a &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;blog&#x2F;integrating-my-screenlocker-with-sxmo-part-1&#x2F;&quot;&gt;blogpost about how screenlocking in sxmo works&lt;&#x2F;a&gt; pre 1.9.0 and post 1.9.0 and how I&#x27;d integrate swaylock-mobile (or any other screenlocker) with it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2023-02-26-curently-abandonned&quot;&gt;2023-02-26 Curently abandonned&lt;&#x2F;h3&gt;
&lt;p&gt;Because of some real-life happenings and some other projects I declare this as officially abandonned, I currently don&#x27;t need my phone. That doesn&#x27;t mean, that I won&#x27;t pick it up again.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2023-05-28-sxmo-swaylock&quot;&gt;2023-05-28 sxmo_swaylock&lt;&#x2F;h3&gt;
&lt;p&gt;KaffeinatedKat merged the swaylock-mobile keypad into his &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;KaffeinatedKat&#x2F;sxmo_swaylock&quot;&gt;sxmo_swaylock&lt;&#x2F;a&gt; project after I &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mortie&#x2F;swaylock-effects&#x2F;issues&#x2F;100&quot;&gt;proposed it to be merged into swaylock-effects&lt;&#x2F;a&gt;. Thank you for adopting it!&lt;&#x2F;p&gt;
&lt;p&gt;Yes I&#x27;m very okay with someone else adoping the code and&#x2F;or idea too!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>My current Smarthome Setup</title>
        <published>2021-08-24T00:00:00+00:00</published>
        <updated>2022-04-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/smarthome/"/>
        <id>https://slatecave.net/creations/smarthome/</id>
        
        <summary type="text">A raspberry pi under my desk that switches screens and audio on and off. commandline edition!</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/smarthome/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;features&quot;&gt;Features&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Can turn GPIOs on and off (I use that for routing audio and switching relays for power outlets)&lt;&#x2F;li&gt;
&lt;li&gt;Pings my machines every few seconds to find out if they are on (I have planned for when that assumption falls flat)&lt;&#x2F;li&gt;
&lt;li&gt;Gemini based API (for keyboard shortcuts) and dashboard&lt;&#x2F;li&gt;
&lt;li&gt;Scriptable using anything that can read from and write to stdio (I use lua)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;documentation&quot;&gt;Documentation&lt;&#x2F;h3&gt;
&lt;p class=&quot;button-paragraph&quot;&gt;
	&lt;a class=&quot;decoration-action-next&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;codeberg.org&amp;#x2F;slatian&amp;#x2F;smarthome&quot;&gt;
		Detailed documentation is in the README
	&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Lunar Widgets - Lua scriptable Gtk Widgets</title>
        <published>2021-07-29T00:00:00+00:00</published>
        <updated>2021-07-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/lunar-widgets/"/>
        <id>https://slatecave.net/creations/lunar-widgets/</id>
        
        <summary type="text">Lua scripting for Gtk without gobject introspection, similar to eww, useful for prototyping</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/lunar-widgets/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;This started out as a simple program to show custom keyboards (the test.lua example is a custom keyboard for newsboat) that could be useful on a touchscreen but is now going in the general direction of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;elkowar&#x2F;eww&quot;&gt;Eww&lt;&#x2F;a&gt; (This project is far away from what Eww can do).&lt;&#x2F;p&gt;
&lt;p&gt;The API is a bit more powerful than documented in the README, it&#x27;s probably best to look at the examples and the sourcecode to find out what else can be done. I once wrote a prototype for a notification display that actually worked (notifications got fed in via stdin).&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>DDB - Object Key Vale Store</title>
        <published>2021-07-11T00:00:00+00:00</published>
        <updated>2021-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/ddb/"/>
        <id>https://slatecave.net/creations/ddb/</id>
        
        <summary type="text">DDB is a simple in memory key value store with the goal to serve as IPC (Inter Process Communication) infrastructure between simple and less simple scripts.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/ddb/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;Usage can include piping system usage information into multiple receiver scripts, storing the last wheater report for offline usage or exposing an interface for a background service. A bit like dbus but a lot simpler.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-does-it-exist&quot;&gt;Why does it exist ?&lt;&#x2F;h2&gt;
&lt;p&gt;There wasn&#x27;t really an easy and obvious way to let two scripts in bash, lua, python or whatever talk to each other, especially in many to many communication scenarios. That&#x27;s why DDB (I wanted to use it for scripts on my desktop) was written in its first version, listening on a TCP socket on localhostand granting access to a hash table and speaking a line based protocol with spaces as field seperators with (horrible) bash scripts as client implementations.&lt;&#x2F;p&gt;
&lt;p&gt;If you are interested in the protocol, its documented in the &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;ddb-object-key-value-store&#x2F;src&#x2F;branch&#x2F;main&#x2F;README.md#protocol&quot;&gt;README&lt;&#x2F;a&gt; and can be considered stable.&lt;&#x2F;p&gt;
&lt;p&gt;Features that I needed in DDB:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Introspection of some kind&lt;&#x2F;li&gt;
&lt;li&gt;Ability to use standard-io for most scripts (that way you don&#x27;t need any fancy debugging tools)&lt;&#x2F;li&gt;
&lt;li&gt;A protocol wich can be implemented in a pretty simple way&lt;&#x2F;li&gt;
&lt;li&gt;Storage integrated into the ipc mechanism&lt;&#x2F;li&gt;
&lt;li&gt;Ability to subscribe to events&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m currently (&lt;time&gt;2023-05-28&lt;&#x2F;time&gt;) using it as the message bus behind &lt;a href=&quot;https:&#x2F;&#x2F;slatecave.net&#x2F;creations&#x2F;smarthome&#x2F;&quot;&gt;my little smarthome&lt;&#x2F;a&gt;, but it is probably going to the attic soon.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Vala project setup script</title>
        <published>2021-05-27T00:00:00+00:00</published>
        <updated>2021-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/vala-project-setup-script/"/>
        <id>https://slatecave.net/creations/vala-project-setup-script/</id>
        
        <summary type="text">This project is a lua script that automates the process of setting up a project with vala, meson and git. Similar to what happens when yo tell cargo or go to create a new project. It also sets up some helper scripts and a README with build instructions.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/vala-project-setup-script/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;When experimenting with gtk and Vala I notices that I was copy-pasting files to bootstrap new projects and experiments all the time, replacing names by hand and … making mistakes. So I put everything into a lua script that helps me with bootstrapping Vala based projects (which at the time of writing, mid 2022 I still do on occasion)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;features&quot;&gt;Features&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;automatic-generation-of-meson-build-files&quot;&gt;Automatic generation of meson.build files&lt;&#x2F;h3&gt;
&lt;p&gt;The script generates two buildfiles for meson, one in the projects root where meson finds it and a second one in the src folder that contains a list of all the files and your dependency list you probably want to extend.&lt;&#x2F;p&gt;
&lt;p&gt;It also gives you a script that you can call to update the list of files to be compiled (list of &lt;code&gt;*.vala&lt;&#x2F;code&gt; files in the src directory) which is good enough for most projects.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;one-command-to-build-and-run&quot;&gt;One command to build and run&lt;&#x2F;h3&gt;
&lt;p&gt;Building with meson is a multi command process, you have to tell meson to generate the build directory then you have to tell ninja to do the actual building, find the result and run it … too many braincycles wasted for my taste, this script gives you a &lt;code&gt;build&lt;&#x2F;code&gt; and a &lt;code&gt;run&lt;&#x2F;code&gt; script that can do all the work for you (the run script invoking the buildscript on every call)&lt;&#x2F;p&gt;
&lt;p&gt;Since you are in charge as soon as the script exits you can do whatever the hell you want with those.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generates-an-initial-readme-md-wich-explains-how-to-use-the-build-system&quot;&gt;Generates an initial README.md wich explains how to use the build-system&lt;&#x2F;h3&gt;
&lt;p&gt;Probably most often asked question by everyone: &lt;q&gt;How do I build that thing?&lt;&#x2F;q&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This thing generates a README.md skeleton for you. (you still have to fill it with information, but at least you don&#x27;t have to copypasta your explanation of how to use meson)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;optionally-generates-a-hello-world-program&quot;&gt;Optionally generates a hello world program&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes its nice to have a bit of code that reminds one of the Language syntax … , this also helps with testing the setup script itself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sets-up-a-git-repository-and-makes-the-initial-commit&quot;&gt;Sets up a git repository and makes the initial commit&lt;&#x2F;h3&gt;
&lt;p&gt;Another two commands that you type in everytime and are everytime the same commands, so while I was at it I automated it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-invocation&quot;&gt;Example invocation&lt;&#x2F;h2&gt;
&lt;figure&gt;
&lt;samp&gt;
&lt;code&gt;$ &lt;kbd&gt;setup_project.lua&lt;&#x2F;kbd&gt;&lt;&#x2F;code&gt;
New Vala Project&lt;br&gt;
This program will setup a new vala project using meson&lt;br&gt;
-------------------------------------------------------&lt;br&gt;
Please enter the name of your project&lt;br&gt;
&amp;gt; &lt;kbd&gt;slate&lt;&#x2F;kbd&gt;&lt;br&gt;
Please enter the reverse domain name of the projects home&lt;br&gt;
&amp;gt; &lt;kbd&gt;net.slatecave&lt;&#x2F;kbd&gt;&lt;br&gt;
Create a main.vala file? (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;y&lt;&#x2F;kbd&gt;&lt;br&gt;
rename.sh is a script that is able to run a single sed command across every *.vala file in the src&#x2F; directory replacing anything with everything, wich is useful for renaming classes and interfaces but can also do a very excellent job at ruining your day without asking if you screw up and don&#x27;t have a backup!&lt;br&gt;
-- If you enable it make sure you have a versioning system ready!! --&lt;br&gt;
Add rename.sh? (Use a versioning&#x2F;backup system!) (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;y&lt;&#x2F;kbd&gt;&lt;br&gt;
Initialize git repository? (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;y&lt;&#x2F;kbd&gt;&lt;br&gt;
Add a remote git repository? (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;y&lt;&#x2F;kbd&gt;&lt;br&gt;
Ok, create your remte repo and give me the (clone) address.&lt;br&gt;
&amp;gt; &lt;kbd&gt;https:&#x2F;&#x2F;example.org&#x2F;git&#x2F;slate&lt;&#x2F;kbd&gt;&lt;br&gt;
-------------------------------------------------------&lt;br&gt;
Project Executable Name: slate&lt;br&gt;
Project id: net.slatecave.slate&lt;br&gt;
Local project folder: &#x2F;home&#x2F;baschdel&#x2F;Development&#x2F;site_generator&#x2F;src&#x2F;projects&#x2F;vala-project-setup-script&lt;br&gt;
+ Add a main.vala file&lt;br&gt;
+ Add the rename.sh bulksed script&lt;br&gt;
+ Initialize a git repository with remote https:&#x2F;&#x2F;example.org&#x2F;git&lt;br&gt;
Is this information correct? (y&#x2F;N)&lt;br&gt;
&amp;gt; &lt;kbd&gt;N&lt;&#x2F;kbd&gt;&lt;br&gt;
Please restart the script then.&lt;br&gt;
&lt;&#x2F;samp&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I hope this helps people understand the code (you shouldn&#x27;t let code that some random wierdo from the Internet wrote manage your projects without making sure that code does what you want).&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>A wrapper for a terraria server with some perks</title>
        <published>2021-05-05T00:00:00+00:00</published>
        <updated>2021-05-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/terraria-server-wrapper/"/>
        <id>https://slatecave.net/creations/terraria-server-wrapper/</id>
        
        <summary type="text">Adds automatic shutdown when not needed, Matterbridge support and exposes Server commands to the ingame chat.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/terraria-server-wrapper/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;how-it-came-into-existence&quot;&gt;How it came into existence&lt;&#x2F;h2&gt;
&lt;p&gt;Long story short: A friend convinced me and a few other friends to try out &lt;a href=&quot;https:&#x2F;&#x2F;terraria.org&#x2F;&quot;&gt;Terraria&lt;&#x2F;a&gt;, we played together using his PC as the server and immedately ran into a strange problem related to us being in germany and demanding bandwidth from our ISPs.&lt;&#x2F;p&gt;
&lt;p&gt;Setting up a server on a cloudserver wasn&#x27;t a big problem, but I wnated something that shut the Server down when nobody used it while we were in School. So I wrote a few lines of python … and called that thing &lt;code&gt;terraria-supervisor.py&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The other features were a: &lt;q&gt;Wouldn&#x27;t it be cool if?&lt;&#x2F;q&gt;&lt;&#x2F;p&gt;
&lt;p&gt;First we got annoyed by the time it took to get dark (or become day again), and I wasn&#x27;t very keen on logging into my Server every time so I added the cheaty part which redirected the server output to the ingame chat and in reverse read the ingame chat and looked for something command like and pipes that into the admin console using a pcall.&lt;&#x2F;p&gt;
&lt;p&gt;The chatty part was me already having a Matterbridge set up for our little community (because I don&#x27;t like Discord) and on the other end having a script that can read and write our ingame chat.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-few-tips-on-game-server-administration&quot;&gt;A few tips on Game Server administration&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately that server didn&#x27;t last very long until it was abandonned, here is the lesson I learned:&lt;&#x2F;p&gt;
&lt;p&gt;Disable cheating and bringing in stuff from the outside (&lt;em&gt;and write that rule along with the reasoning down somewhere!&lt;&#x2F;em&gt;), someone will eventually join with their level 9001 character or turn the cheatclient on, you all have a couple of hours of fun beat that one boss in no time at all and then be greeted with a gameworld that is too difficult for everyone playing fair (and boring for the others), a rollback feels like loosing progress, both options kill the world on the server and the players (including YOU, the admin) won&#x27;t come back.&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Dragonstone - Gemini&#x2F;Gopher Browser</title>
        <published>2019-11-03T00:00:00+00:00</published>
        <updated>2019-11-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/dragonstone/"/>
        <id>https://slatecave.net/creations/dragonstone/</id>
        
        <summary type="text">Dragonstone is a browser for the Gopher and Gemini protocols built with Gtk 3.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/dragonstone/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;It started as a Gopher browser inspired by the Pocket Gopher Android App, later support for the gemini and finger protocols was added. It also has theming support for documents. I use it as my primary gopher&#x2F;gemini browser.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-little-background-and-history&quot;&gt;A little background and history&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;so-why-does-someone-write-a-browser-at-all&quot;&gt;So why does someone write a browser at all?&lt;&#x2F;h3&gt;
&lt;p&gt;The project started as a coincidence: At the end of 2019 I lwaenewd about a protocol called gopher and started exploring some gopherholes … On an Android Phone with an app called Pocket Gopher which looked nice, was useable but had no cache at all and I wanted a desktop app anyway. At the same time I found a &lt;a href=&quot;https:&#x2F;&#x2F;youtube.com&#x2F;playlist?list=PLriKzYyLb28mn2lS3c5yqMHgLREi7kR9-&quot;&gt;Vala Tutorial by Alessandro Castellani&lt;&#x2F;a&gt; and needed a project to turn all the things I&#x27;ve learned into something useful …&lt;&#x2F;p&gt;
&lt;p&gt;While the original plan was to write a gopher browser with local file browsing capabilities I discovered the &lt;a href=&quot;https:&#x2F;&#x2F;gemini.circumlunar.space&#x2F;&quot;&gt;Gemini protocol&lt;&#x2F;a&gt;, and wrote &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;dragonstone&#x2F;commit&#x2F;b66b58e23f6d9962719507355ecf007f227f7505&quot;&gt;some extra code to handle gemini requests&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Fun fact: A few days later Julien Blanchard started working on the &lt;a href=&quot;https:&#x2F;&#x2F;sr.ht&#x2F;~julienxx&#x2F;Castor&#x2F;&quot;&gt;Castor browser&lt;&#x2F;a&gt;, where I later copied the idea to use a GtkTextView for document rendering (dragonstones terrible link implementation was my idea to get rid of some performance issues with the button in textview approach).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;request-infrastructure&quot;&gt;Request infrastructure&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;b&gt;Or: Why making a browser is easy but hard to get right&lt;&#x2F;b&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The request infrastructure of Dragonstone was originally based on the idea that a request with an uri would be passed tp a resource store, processed and then be returned to the tab wich selects a rendering widget based on the success and file type. (simple gopher browser with the capability to view local files)&lt;&#x2F;p&gt;
&lt;p&gt;Letting the widgets see (and touch, uploading in dragonstone is a giant hack) the requests was a GIGANTIC mistake, UI code should NEVER do any application logic …&lt;&#x2F;p&gt;
&lt;p&gt;It also turned out (earlier actually) that thinking in therms of resource stores was also a flawed Idea, caching is required across almost all networking protocols and sometimes a resource store needs extra information like a certificate, the &amp;quot;session&amp;quot; was born a rewrite of a lot of code later to have this functionality in seperate blocks.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;failed-rewrites&quot;&gt;Failed rewrites&lt;&#x2F;h3&gt;
&lt;p&gt;There were two attempts to rewrite Dragonstone:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;browser-experiment.nightcat&quot;&gt;The Nightcat Experiment&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;slatian&#x2F;browser-experiment.slate&quot;&gt;The Slate Experiment&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;(Note that I&#x27;ll reuse both names and I&#x27;ll refer to them as experiments and eventually update the git repo. You probybly have noticed, that I reused Slate for this website and my new nick)&lt;&#x2F;p&gt;
&lt;p&gt;Nightcat was an excellent Request framework, except that I realized too late that When streaming resurces there could be a result before the final result and that killed it because it was built with the assumption that the browser waits for the result with only a progress indicator.&lt;&#x2F;p&gt;
&lt;p&gt;Slate on literally the other end reused Dragonstones flawed request framework in an attempt to decouple the UI from the requests entirely before eventually replacing the requests framework with a better one. It even reused Dragonstones document renderer, unfortunately this approach takes time and I put it on hold in favour of some other project. (It is unuseable in its current state, but maybe I&#x27;ll pick it back up some day)&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>A Tetris clone</title>
        <published>2019-05-21T00:00:00+00:00</published>
        <updated>2019-05-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/tetris/"/>
        <id>https://slatecave.net/creations/tetris/</id>
        
        <summary type="text">A simple Tetris game written in Python that started as a scool exercise.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/tetris/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;p&gt;It uses the &lt;a href=&quot;https:&#x2F;&#x2F;cs1graphics.org&#x2F;&quot;&gt;cs1graphics library&lt;&#x2F;a&gt; for painting and is the project I learned python with.&lt;&#x2F;p&gt;
&lt;p&gt;Information about gow to play and a little overview for Modding the game is in the README.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-bit-about-the-engine&quot;&gt;A bit about the Engine&lt;&#x2F;h2&gt;
&lt;p&gt;The engine is split into two parts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The renderer which gets an array of squares the preview, score and some text for an optional overlay and draws this into a window.&lt;&#x2F;li&gt;
&lt;li&gt;The other part is the game mechanics part which implements the game logic.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The tile rotations are calculated on the fly from an array of relative square positions, so if you want to try out some crazy shapes go ahed (The function is a generator so in theory one could also generate crazy tiles on the fly)&lt;&#x2F;p&gt;
&lt;p&gt;Because of the split theming (alias changing the colors) is also pretty simple. Also I remember the black-white version looking rad!&lt;&#x2F;p&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>A simple TicTacToe game</title>
        <published>2018-07-07T00:00:00+00:00</published>
        <updated>2018-07-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Slatian
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slatecave.net/creations/tic-tac-toe/"/>
        <id>https://slatecave.net/creations/tic-tac-toe/</id>
        
        <summary type="text">Written for the commandline this is my first game I&#x27;ve written myself, still fun to have around in 2020.</summary>
        
        <content type="html" xml:base="https://slatecave.net/creations/tic-tac-toe/">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;how-to-play&quot;&gt;How to play&lt;&#x2F;h2&gt;
&lt;figure&gt;
&lt;figcaption&gt;A sceenshot of a game currently in progress, playing field rendered in ascii-art at the top, a little guide which input number corresponds with wich field and a prompt asking O for their move.&lt;&#x2F;figcaption&gt;
&lt;pre&gt;&lt;samp&gt;
                 ### ##\          &#x2F;## ###                 
                 ###  \##\      &#x2F;##&#x2F;  ###                 
                 ###    \##\  &#x2F;##&#x2F;    ###                 
                 ###      \####&#x2F;      ###                 
                 ###      &#x2F;####\      ###                 
                 ###    &#x2F;##&#x2F;  \##\    ###                 
                 ###  &#x2F;##&#x2F;      \##\  ###                 
                 ### ##&#x2F;          \## ###                 
##########################################################
##########################################################
                 ###                  ### ##\          &#x2F;##
                 ###                  ###  \##\      &#x2F;##&#x2F; 
                 ###                  ###    \##\  &#x2F;##&#x2F;   
                 ###                  ###      \####&#x2F;     
                 ###                  ###      &#x2F;####\     
                 ###                  ###    &#x2F;##&#x2F;  \##\   
                 ###                  ###  &#x2F;##&#x2F;      \##\ 
                 ###                  ### ##&#x2F;          \##
##########################################################
##########################################################
##\          &#x2F;## ###    __________    ###    __________   
 \##\      &#x2F;##&#x2F;  ###   &#x2F; ________ \   ###   &#x2F; ________ \  
   \##\  &#x2F;##&#x2F;    ###  &#x2F; &#x2F;        \ \  ###  &#x2F; &#x2F;        \ \ 
     \####&#x2F;      ### | |          | | ### | |          | |
     &#x2F;####\      ### | |          | | ### | |          | |
   &#x2F;##&#x2F;  \##\    ### | |          | | ### | |          | |
 &#x2F;##&#x2F;      \##\  ###  \ \________&#x2F; &#x2F;  ###  \ \________&#x2F; &#x2F; 
##&#x2F;          \## ###   \__________&#x2F;   ###   \__________&#x2F;  
789
456
123
O&#x27;s turn:
&lt;&#x2F;samp&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The game is pretty simple, X starts and then X and O make their moves by entering the number that corresponds to the field they want to place their marker on, if one of then gets a row of three they win, if the field fills up dbefore that happens the game is a draw.&lt;&#x2F;p&gt;
&lt;p&gt;Hint: Your Numpad probably has the same layout as the field numbers!&lt;&#x2F;p&gt;
</content>
    </entry>
</feed>

