<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: David Cantrell</title>
    <description>The latest articles on Forem by David Cantrell (@drhyde).</description>
    <link>https://forem.com/drhyde</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F98675%2F87292a43-3bd4-47dd-8169-4683aad0aceb.jpeg</url>
      <title>Forem: David Cantrell</title>
      <link>https://forem.com/drhyde</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/drhyde"/>
    <language>en</language>
    <item>
      <title>G̶o̶d̶ ̶i̶s̶ ̶m̶y̶ ̶C̶o̶p̶i̶l̶o̶t̶ Copilot is my new god</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Mon, 27 Apr 2026 17:06:51 +0000</pubDate>
      <link>https://forem.com/drhyde/god-is-my-copilot-copilot-is-my-new-god-5h6i</link>
      <guid>https://forem.com/drhyde/god-is-my-copilot-copilot-is-my-new-god-5h6i</guid>
      <description>&lt;p&gt;I spent some time this weekend noodling around with Github Copilot, using the &lt;a href="https://github.com/features/copilot/cli" rel="noopener noreferrer"&gt;copilot-cli tool&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A while back I had created a little Electron app that searched for and displayed information about Unicode characters. I didn’t really know what I was doing, either with Electron or Javascript, and couldn’t find much in the way of correct and up-to-date beginner-level documentation on things as simple as how to wire an application together - the doco was good enough for a "Hello World" but nothing beyond that. As an aside, I have the same gripe about lots of languages and frameworks - good luck figuring out how to drive things like make and the linker from any online C documentation. Anyway, I muddled through, and you can see what I did by hand &lt;a href="https://github.com/DrHyde/unicode-search/tree/f340bdb68703f7d0a62ab200f1d4586217f9b4e9" rel="noopener noreferrer"&gt;here&lt;/a&gt;. It &lt;em&gt;works&lt;/em&gt;, and the same code produces both an Electron app, and a stand-alone web page, and a CLI tool, but it’s &lt;em&gt;vile&lt;/em&gt;, especially the &lt;a href="https://github.com/DrHyde/unicode-search/blob/f340bdb68703f7d0a62ab200f1d4586217f9b4e9/Makefile" rel="noopener noreferrer"&gt;build process&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The updates since then are &lt;a href="https://github.com/DrHyde/unicode-search/compare/109ddae69fdd007bc28d4f36ae70da9d35132785...master" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Copilot wrote pretty much all of that with only a few very small manual tweaks. The initial prompt that produced most of the work was something like “Make this project more idiomatically Electron and Javascript-ish”. I worked with the evil hallucination engine for a few hours, including prompting it to add tests and a Github workflow for CI. I mostly gave it its head and let it do whatever it wanted although there were a few small things where I told it to do things slightly differently. Overall, I was very impressed with the result. With my lack of knowledge of modern HTML/CSS and Javascript there’s no way I could have done this myself.&lt;/p&gt;

&lt;p&gt;I then thought “there’s so much Javascript/Electron stuff out there for it to have learned from, let’s try something different” so I took the next little tool improvement on my to-do list and asked it to write it for me. I’d previously had a shell alias &lt;code&gt;alias highlight="ack --color --passthru"&lt;/code&gt; - you pipe stuff in, give it a perl regex, and it highlights anything matching. I wanted to be able to control the colours, by saying something like &lt;code&gt;cal | highlight --red '25|26' --green '27|28'&lt;/code&gt;. So I prompted copilot with something along the lines of “write a shell script that will take an optional &lt;code&gt;--color_name&lt;/code&gt; parameter followed by a perl regex, read input from STDIN, and spit it to STDOUT with any matches highlighted in that colour. It should allow any number of such matches”. It took a few attempts, mostly it iterating towards a solution while it tested and re-tested what it had done, and required very little in the way of corrective input from me. It also wrote the documentation when I told it to. I thought I’d try giving it something that is confusing as hell even for experienced shell scripters, and asked it to make the script able to spit out its own shell tab-completion function, but it managed that too. I can now type &lt;code&gt;highlight --gr&amp;lt;tab&amp;gt;&lt;/code&gt; and it auto-completes &lt;code&gt;green&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For another little task I gave it in perl, I told it to make sure that all the changes it made to an existing code base were 100% covered by tests, including branch and condition coverage. It went away and did that. In fact it also wrote some missing tests for stuff that I had previously written. And then it wrote a test that will fail if I ever make code changes in the future that aren’t 100% covered.&lt;/p&gt;

&lt;p&gt;At various points it said things like “I need to consult the documentation” and (after asking permission) went off and read the docs for things like &lt;a href="https://metacpan.org/pod/Devel::Cover" rel="noopener noreferrer"&gt;Devel::Cover&lt;/a&gt;, and the Electron docs. It even said “I can’t figure this out from the Electron docs” (oh good, it’s not just me that thought it wasn’t very good!) and went and read the Electron source code to figure out what’s what.&lt;/p&gt;

&lt;p&gt;I was on a bit of a roll by now, so I asked it to fix a long-standing bug in one of my perl modules, which consistently failed its tests on OpenBSD, NetBSD and OmniOS, and intermittently failed on FreeBSD, while always passing on Linux and Mac. I’ve been banging my head on this for over a year, and no-one I’ve asked for help has been able to figure it out either. It looked at the code, and at the failure reports in the OpenBSD test workflow on Github, and told me what the problem was and how to fix it. I didn’t believe it, so I said “verify that, here’s the address of an OpenBSD machine you can ssh to”. So it did. I wasn’t happy with its suggested fix - it would have fixed the test, but subtly broken interactive use of the tool by buffering STDERR output until vomiting it all up at the end - so I explained that to it and it did a massive rewrite of my code. I was rather dubious but having tested it myself and very carefully reviewed what it did - yep, it got it right. The &lt;a href="https://metacpan.org/release/DCANTRELL/App-ptimeout-1.0.3/diff/DCANTRELL/App-ptimeout-1.0.2" rel="noopener noreferrer"&gt;code it produced&lt;/a&gt; is a little hard to read, but that’s because it’s dealing with some complicated inter-process communication and control. It wrote tests without prompting. Perhaps my only criticism is that it didn’t comment the code.&lt;/p&gt;

&lt;p&gt;This thing is FUCKING MAGIC. I will be using it a lot for creating new stuff, and for updating small code bases. I don’t think it stands a chance in hell of being particularly useful for the software archaeology part of maintaining a large legacy application - which is what most devs spend most of the time doing at work - but I am going to at least try using it to make small updates once I have figured out what/where they should be. The quality of the code it produces is fairly good. Many of us could of course do better, but what it produces is generally readable. Copilot does require a little hand-holding and its output needs a light editing pass before it’s worthy of being put up for code review by other humans, but no more than the code I write myself does.&lt;/p&gt;

&lt;p&gt;I strongly recommend that you give it a go.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Automatic cross-platform testing: part 7: 32 bit, again</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Fri, 27 Feb 2026 21:23:41 +0000</pubDate>
      <link>https://forem.com/drhyde/automatic-cross-platform-testing-part-7-32-bit-again-1ipf</link>
      <guid>https://forem.com/drhyde/automatic-cross-platform-testing-part-7-32-bit-again-1ipf</guid>
      <description>&lt;p&gt;I have &lt;a href="https://dev.to/drhyde/automatic-cross-platform-testing-part-6-32-bit-linux-fh2"&gt;written about this before&lt;/a&gt;, but I've had to do it again. I noted previously that Github's actions for things like checking out a repository and for retrieving build artifacts are now 64-bit only. You've been able to work around that by using older versions of the actions but I didn't want to do that so I'm taking a different approach.&lt;/p&gt;

&lt;p&gt;My new method builds on work I've done previously to pull all my testing for different Unixy platforms together into one workflow. &lt;a href="https://github.com/DrHyde/perl-modules-Scalar-Type/blob/38e64fe918da7feee0b0e7dba39a6be00bdc0ddb/.github/workflows/install-various-OSes.yml" rel="noopener noreferrer"&gt;This&lt;/a&gt; tests on 64-bit versions of NetBSD, FreeBSD, OpenBSD, IllumOS (that is, the modern version of Open Solaris), and Linux. And as of earlier today it also runs the tests on 32-bit Linux. As in my previous blog entry it's still really 64-bit hardware, with modern x86 ISA extensions, but it's a 32-bit OS image with 32-bit addressing, userland, compiler toolchain and so on. If you absolutely must have a pure 32-bit x86 environment then you'll need to acquire some obsolete e-waste and run it yourself. Something like &lt;a href="https://icop-shop.com/product/ebox-3352dx3-gl/" rel="noopener noreferrer"&gt;this&lt;/a&gt; based on the Vortex86 chipset looks like a good choice.&lt;/p&gt;

&lt;p&gt;There are only two interesting bits of the workflow. The first implements enough of Github's &lt;code&gt;actions/download-artifact&lt;/code&gt; for my purposes. In the YAML file it's all one line because YAML and everything that uses it is hateful, I've tidied it up here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -L -H "X-GitHub-Api-Version: 2022-11-28"
        -H "Authorization: Bearer ${GH_TOKEN}"
        -o dist-for-install.zip
  $(curl -L -s
         -H "Accept: application/vnd.github+json"
         -H "Authorization: Bearer ${GH_TOKEN}"
         -H "X-GitHub-Api-Version: 2022-11-28"
         https://api.github.com/repos/${GH_REPOSITORY}/actions/artifacts
         -o -
    |jq -r '
         [.artifacts.[]|select(.expired == false)]
         |max_by(.created_at).archive_download_url
    '
  )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The inner &lt;code&gt;curl&lt;/code&gt; uses the &lt;a href="https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-artifacts-for-a-repository" rel="noopener noreferrer"&gt;Github API&lt;/a&gt; to fetch a list of all the build artifacts that have ever been created for this repository, as JSON, which it filters using &lt;code&gt;jq&lt;/code&gt; to find the most recent one which hasn't expired, and then extract its &lt;code&gt;archive_download_url&lt;/code&gt; field. The outer &lt;code&gt;curl&lt;/code&gt; then fetches that.&lt;/p&gt;

&lt;p&gt;The second bit interesting bit is specific to testing my perl code. Perl can be built on 32-bit platforms to support either 32 or 64 bit integers. On 64-bit machines perl integers are always 64-bit - and there's been a note in the docs for 20 years about how a build-time parameter for 32-bit ints may be added later! I need to test with 32 bit ints, but most OS packages, even on 32-bit machines, will provide a perl with 64-bit ints. So I need to build my own. This is dead simple if a little wordy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -o perl-5.42.0-src.tar.gz https://cpan.metacpan.org/authors/id/B/BO/BOOK/perl-5.42.0.tar.gz
tar xzf perl-5.42.0-src.tar.gz
cd perl-5.42.0
sh Configure -de -Dprefix=$HOME/perl-5.42.0-installed
make -j $(nproc)
make install
$HOME/perl-5.42.0-installed/bin/perl -MCPAN -e 'install qw(App::cpanminus)'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The call to the &lt;code&gt;Configure&lt;/code&gt; script is where the OS packaging people make it have 64-bit ints by adding the &lt;code&gt;-Duse64bitint&lt;/code&gt; flag. Normally you would &lt;code&gt;make test&lt;/code&gt; too but I've satisfied myself that it does indeed pass its tests using this Docker image, so I skip that step to save a lot of time.&lt;/p&gt;

&lt;p&gt;Finally, I can extract my software and test it. It is bloody irritating that Github automatically wrap my tarball in the obsolete &lt;code&gt;zip&lt;/code&gt; format. I blame Microsoft:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unzip dist-for-install.zip
mkdir dist-for-test
tar -C dist-for-test -xzf *.tar.gz
cd dist-for-test/*
$HOME/perl-5.42.0-installed/bin/cpanm --installdeps .
$HOME/perl-5.42.0-installed/bin/perl Makefile.PL
make test TEST_VERBOSE=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there you have it - how to test your code automatically, using Github actions, on a 32-bit platform.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>githubactions</category>
      <category>ci</category>
      <category>32bit</category>
    </item>
    <item>
      <title>Unicode-Search - my first Electron app!</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Sat, 23 Nov 2024 11:35:46 +0000</pubDate>
      <link>https://forem.com/drhyde/unicode-search-my-first-electron-app-41op</link>
      <guid>https://forem.com/drhyde/unicode-search-my-first-electron-app-41op</guid>
      <description>&lt;p&gt;I am a Unix greybeard who mostly works in perl and shell programming, with a bit of dabbling in C and Rust, but for a long time I've wanted to do some Javascript. I just needed an interesting enough project to use it in. Well, at work I spend a lot of time toiling in the Unicode mines, fixing mojibÃ©ke errors in a somewhat elderly code base. As part of that I wrote &lt;a href="https://dev.to/drhyde/a-brief-guide-to-perl-character-encoding-if7"&gt;a guide&lt;/a&gt; for my colleagues, which points them at a very useful &lt;a href="http://xahlee.info/comp/unicode_index.html" rel="noopener noreferrer"&gt;reference website&lt;/a&gt; run by Xah Lee. That has been around for years, but as the single maintainer of several projects myself I am wary of relying too much on &lt;a href="https://www.xkcd.com/2347/" rel="noopener noreferrer"&gt;projects with a single maintainer&lt;/a&gt;. So I decided to write my own, and that this would be my interesting little Javascript project.&lt;/p&gt;

&lt;p&gt;It seems like a sensible choice for learning a new language, as it's not doing much, just looking up data in a static structure and displaying it to the user. It's all synchronous code, there's no I/O beyond updating a web page, and the user interface can be dead simple.&lt;/p&gt;

&lt;p&gt;Electron's &lt;a href="https://www.electronjs.org/docs/latest/tutorial/quick-start" rel="noopener noreferrer"&gt;quick start guide&lt;/a&gt; is excellent and gave me the basic boilerplate for a "Hello World" application, and then the rest was just lots (and lots, and lots) of looking things up on Stack Overflow and &lt;a href="https://www.w3schools.com/js/default.asp" rel="noopener noreferrer"&gt;W3Schools&lt;/a&gt;. From start to finish it took about 6 hours, and &lt;a href="https://github.com/DrHyde/unicode-search" rel="noopener noreferrer"&gt;my code is on Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb476po3alvmd6wwqowij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb476po3alvmd6wwqowij.png" alt="Screenshot of my little app running" width="600" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm quite sure that those of you skilled in the ways of Javascript will find the code "idiosyncratic", but that's OK, these are baby's first steps. If you have any useful tips for improving it, and can explain them &lt;em&gt;simply&lt;/em&gt;, then they would be most welcome. I would also welcome pull requests that make it stop looking ugly as sin. Learning how to make rounded corners and stuff in CSS wasn't in scope :-)&lt;/p&gt;

&lt;p&gt;Update: because working on the command line is &lt;em&gt;always&lt;/em&gt; better than using a GUI, it has sprouted a CLI tentacle. The same code does most of the work of parsing user input and looking up characters, it just has a different function for spitting the results back out to the user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcq4df917ks2lardybry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcq4df917ks2lardybry.png" alt="Image description" width="682" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>electron</category>
      <category>javascript</category>
      <category>unicode</category>
    </item>
    <item>
      <title>Programmable tab completion with bash</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Sun, 10 Nov 2024 23:27:15 +0000</pubDate>
      <link>https://forem.com/drhyde/programmable-tab-completion-with-bash-5ama</link>
      <guid>https://forem.com/drhyde/programmable-tab-completion-with-bash-5ama</guid>
      <description>&lt;p&gt;We all use tab completion in the shell, but have you stopped to think about how it works? How with one command it will auto-complete only directory names, with another directories and filenames, but with another it will complete branch names for your VCS, for example? It's programmable! And so we can bend it to our will!&lt;/p&gt;

&lt;p&gt;I recently had an annoyance. I use a tool called &lt;a href="https://viric.name/soft/ts/" rel="noopener noreferrer"&gt;ts&lt;/a&gt; (task spooler) to run some CPU-intensive processes in the background, and to queue them so that only one such process runs at once. It is invoked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ts command arg1 arg2 arg...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted to be able to use tab completion for the &lt;code&gt;command&lt;/code&gt;, and to then use &lt;em&gt;that command's&lt;/em&gt; tab completion for subsequent arguments. After much swearing and cursing - the documentation in the &lt;code&gt;bash&lt;/code&gt; manpage is not the best - and a bit of help from some nice people &lt;a href="https://stackoverflow.com/questions/79173765/how-to-include-completions-for-one-command-in-those-of-another" rel="noopener noreferrer"&gt;on Stack Overflow&lt;/a&gt; I came up with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; bashdefault &lt;span class="nt"&gt;-o&lt;/span&gt; default &lt;span class="nt"&gt;-F&lt;/span&gt; __ts_bash_completions ts

__ts_bash_completions &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COMP_CWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[COMP_CWORD]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;command_completion_function&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; 2&amp;gt;/dev/null|sed &lt;span class="s1"&gt;'s/.*-F \([^ ]*\) .*/\1/'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$command_completion_function&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nv"&gt;COMP_CWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; COMP_CWORD &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;
            &lt;span class="nv"&gt;COMP_LINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$COMP_LINE&lt;/span&gt;|sed &lt;span class="s2"&gt;"s/^&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; //"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
            &lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;:1&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;

            &lt;span class="nv"&gt;$command_completion_function&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;fi
    fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go through it in detail. The first line tells the shell to use the function &lt;code&gt;__ts_bash_completions&lt;/code&gt; when the user is typing the &lt;code&gt;ts&lt;/code&gt; command and its subsequent arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;complete -F __ts_bash_completions ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then define that function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;__ts_bash_completions () {
    COMPREPLY=()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the first thing we do is create an empty array &lt;code&gt;COMPREPLY&lt;/code&gt;. &lt;code&gt;bash&lt;/code&gt; completions populate this global variable to tell the shell what options are available. We then see how many complete words there are on the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    if [ "$COMP_CWORD" -eq 1 ]; then
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;COMP_CWORD&lt;/code&gt; is another global variable that contains the number of complete words currently in the command. If that is 1 then the only word currently in the command is &lt;code&gt;ts&lt;/code&gt; itself, we want to autocomplete the name of a command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        COMPREPLY=($(compgen -c -- "${COMP_WORDS[COMP_CWORD]}"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;compgen&lt;/code&gt; (&lt;code&gt;comp&lt;/code&gt;letion &lt;code&gt;gen&lt;/code&gt;erator) generates a list of all the commands available in the &lt;code&gt;$PATH&lt;/code&gt; which begin with &lt;code&gt;${COMP_WORDS[COMP_CWORD]}&lt;/code&gt;. This introduces yet another magic global variable, &lt;code&gt;COMP_WORDS&lt;/code&gt; is an array (zero indexed) of all the words currently on the command line, including the one currently being typed, which may be empty. We pick the last one and pass that to &lt;code&gt;compgen&lt;/code&gt; for it to use as a filter.&lt;/p&gt;

&lt;p&gt;At this point we've tab completed the name of the command that &lt;code&gt;ts&lt;/code&gt; is to run and deserve a beer. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fammk7zx1viqmu3ble72q.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fammk7zx1viqmu3ble72q.JPG" alt="Cheers!" width="800" height="1127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to deal with tab completion of the arguments to the command that we want &lt;code&gt;ts&lt;/code&gt; to run. First we need to find what function is used for completions for that command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    else
        local command_completion_function="$(complete -p ${COMP_WORDS[1]} 2&amp;gt;/dev/null|sed 's/.*-F \([^ ]*\) .*/\1/')"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;complete -p&lt;/code&gt; tells you exactly how tab completion is configured for a given command, and spits it out in the form of the command to use to configure it. The function will be the argument to the &lt;code&gt;-F&lt;/code&gt; option, so we use a rather crude &lt;code&gt;sed&lt;/code&gt; invocation to extract it, if it is present. The resulting string will be empty if no completion function is defined. We then check that it isn't empty and play around with the contents of the various &lt;code&gt;COMP_*&lt;/code&gt; variables, setting them up to pretend that &lt;code&gt;ts&lt;/code&gt; isn't involved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        if [ ! -z "$command_completion_function" ]; then
            COMP_CWORD=$(( COMP_CWORD - 1 ))
            COMP_LINE=$(echo $COMP_LINE|sed "s/^${COMP_WORDS[0]} //")
            COMP_WORDS=( "${COMP_WORDS[@]:1}" )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We decrement &lt;code&gt;COMP_CWORD&lt;/code&gt; because we want to ignore one of the completed words - that being &lt;code&gt;ts&lt;/code&gt; itself. We remove &lt;code&gt;ts&lt;/code&gt; from the start of &lt;code&gt;COMP_LINE&lt;/code&gt; - which is a string containing the entire line of input. And we remove the first element from the &lt;code&gt;COMP_WORDS&lt;/code&gt; array. Finally, we run the command's own completion function which will set &lt;code&gt;COMPREPLY&lt;/code&gt; for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            $command_completion_function "${COMP_WORDS[0]}" "$2" "$3"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It appears that the calling convention for completion functions has changed over time. At some point in the past they were called with arguments, which only contain the command name and the last two arguments on the command line, then they were changed to instead use those global variables. In this case we can just use the arguments that were passed to our function altering only the first one, the name of the command whose arguments we are completing. Depending on how modern the completion function is for a given command, our &lt;code&gt;ts&lt;/code&gt; completion needs to support both calling conventions.&lt;/p&gt;

&lt;p&gt;There's one final wrinkle, which the observant amongst you may have noticed when I started going through this line by line. In my script I have &lt;code&gt;-o bashdefault -o default&lt;/code&gt; when defining the relationship between &lt;code&gt;ts&lt;/code&gt; and its completion function. Those are to cope with the case where the completion function leaves &lt;code&gt;COMPREPLY&lt;/code&gt; empty. If that happens then &lt;code&gt;-o bashdefault&lt;/code&gt; applies &lt;code&gt;bash&lt;/code&gt;'s own defaults for tab completion, ie helps you pick a filename, and if that doesn't return anything &lt;code&gt;-o default&lt;/code&gt; applies &lt;code&gt;readline&lt;/code&gt;'s defaults. You can see this in action for &lt;code&gt;git&lt;/code&gt;'s completions. If you type &lt;code&gt;git log &amp;lt;tab&amp;gt;&lt;/code&gt; in a git repo you get a list of branch names, and if you type &lt;code&gt;git log foo&amp;lt;tab&amp;gt;&lt;/code&gt; you get a list of branches whose names begin with &lt;code&gt;foo&lt;/code&gt;. But if there are no such branches you get a list of &lt;em&gt;files&lt;/em&gt; whose names begin with &lt;code&gt;foo&lt;/code&gt;, which is the shell's default action.&lt;/p&gt;

&lt;p&gt;Anyway, after a great deal of wrestling with poor documentation, that's a little annoyance dealt with, and exactly the same code can also be used for other similar commands such as &lt;code&gt;sudo&lt;/code&gt; and &lt;code&gt;nohup&lt;/code&gt;. If you think you'll find it useful then the code is &lt;a href="https://github.com/DrHyde/configurations/blob/c4e881670ec21a61ac1eac6cab296c5d8771bcce/bash/dot-bash_completion.d/ts" rel="noopener noreferrer"&gt;on Github&lt;/a&gt; and it's also in the &lt;code&gt;ts&lt;/code&gt; &lt;a href="https://viric.name/wsgi-bin/hgweb.wsgi/ts/rev/9e80ac43e6d2" rel="noopener noreferrer"&gt;mercurial repo&lt;/a&gt; so will no doubt be in a future release.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>shell</category>
    </item>
    <item>
      <title>The typeface you didn't know you wanted and were trained to hate</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Tue, 05 Sep 2023 14:59:32 +0000</pubDate>
      <link>https://forem.com/drhyde/the-typeface-you-didnt-know-you-wanted-and-were-trained-to-hate-5f3f</link>
      <guid>https://forem.com/drhyde/the-typeface-you-didnt-know-you-wanted-and-were-trained-to-hate-5f3f</guid>
      <description>&lt;p&gt;For the last several weeks I've been using &lt;a href="https://dtinth.github.io/comic-mono-font/"&gt;Comic Mono&lt;/a&gt; in my terminal. It's a fixed width typeface based on the font that we've all been trained to despise and sneer at for almost 30 years, Comic Sans.&lt;/p&gt;

&lt;p&gt;But you know what? Comic Mono works really well at small sizes even with my becoming-elderly failing eyes and my inability to always find my reading glasses. I can see plenty of code in my editor with lots of context. On a 40" screen at arm's length I can comfortably get 100 lines of text - so lots of context for the code I'm working on - in a terminal using 12pt text &lt;em&gt;and read it easily&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Give it a go for a few days!&lt;/p&gt;

</description>
      <category>ui</category>
      <category>font</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Number::Phone is now 64-bit only</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Tue, 20 Jun 2023 10:40:02 +0000</pubDate>
      <link>https://forem.com/drhyde/numberphone-is-now-64-bit-only-eje</link>
      <guid>https://forem.com/drhyde/numberphone-is-now-64-bit-only-eje</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/drhyde/deprecating-32-bit-perl-3512"&gt;A couple of years ago&lt;/a&gt; I wrote that I would be dropping support for perls with 32-bit integers in Number::Phone. Well, that two year deprecation cycle is up, and last night I switched over to the &lt;a href="https://metacpan.org/dist/Data-CompactReadonly/view/lib/Data/CompactReadonly/V0/Format.pod"&gt;new database format&lt;/a&gt;, the software for which requires 64-bit integers. As of the next release it will use under 10MB of disk space instead of about 100. An order of magnitude improvement, and it was fun to write as well.&lt;/p&gt;

&lt;p&gt;Perl has supported 64-bit integers on all reasonable platforms for the last 20 years, even on those platforms which still use 32-bit pointers, so I don't expect that anyone will notice any change at all except that their hosted Docker containers will now be &lt;a href="https://github.com/DrHyde/perl-modules-Number-Phone/issues/95"&gt;faster to deploy and cheaper to run&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>perl</category>
      <category>32bit</category>
      <category>deprecation</category>
      <category>64bit</category>
    </item>
    <item>
      <title>cpulimit annoyed me so I improved it</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Sat, 25 Mar 2023 19:07:07 +0000</pubDate>
      <link>https://forem.com/drhyde/cpulimit-annoyed-me-so-i-improved-it-5c4n</link>
      <guid>https://forem.com/drhyde/cpulimit-annoyed-me-so-i-improved-it-5c4n</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/drhyde/gnu-timeout-annoyed-me-so-i-replaced-it-or-an-extremely-simple-introduction-to-fork-and-signal-handling-in-perl-32f7"&gt;Previously&lt;/a&gt; | &lt;a href="https://dev.to/drhyde/gnu-tree-annoyed-me-so-i-fixed-it-2pnk"&gt;more previously&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few days ago I discovered &lt;a href="https://github.com/opsengine/cpulimit"&gt;&lt;code&gt;cpulimit&lt;/code&gt;&lt;/a&gt;. It's a great tool that nicely (haha) complements &lt;code&gt;nice&lt;/code&gt;. Where &lt;code&gt;nice&lt;/code&gt; is normally used to reduce the amount of CPU a process uses by changing it priority, a &lt;code&gt;nice&lt;/code&gt;d process can still end up using more CPU than you want, and will of course use all that it wants if nothing with a higher priority comes along.&lt;/p&gt;

&lt;p&gt;But sometimes you want to restrict a process to using no more than some particular fraction of CPU time regardless of priority. A good example is when you don't want those noisy PC fans to kick in and you don't care how long a job takes because whether it finishes in 15 minutes or 8 hours it's still going to finish while you're asleep.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cpulimit&lt;/code&gt; is perfect for this, and is simple to use. An invocation like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cpulimit &lt;span class="nt"&gt;-l&lt;/span&gt; 50 somecommand ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will run &lt;code&gt;somecommand&lt;/code&gt; but restrict it to only use 50% of a CPU. It does this by forking a watchdog process that periodically checks to see how much work &lt;code&gt;somecommand&lt;/code&gt; is doing, and if it's used too much briefly pauses it by sending the &lt;code&gt;STOP&lt;/code&gt; signal. After a little while it will un-pause it with the &lt;code&gt;CONT&lt;/code&gt; signal. Of course, because the load on your machine from other processes will never quite be constant, &lt;code&gt;cpulimit&lt;/code&gt; rarely hits the target exactly, but it gets close enough.&lt;/p&gt;

&lt;p&gt;But I wanted to tweak it a bit. I wanted to be able to interactively "turn the volume knob" so that I could give &lt;code&gt;somecommand&lt;/code&gt; more or less CPU whenever I fancied. The result is &lt;a href="https://github.com/opsengine/cpulimit/pull/116/files"&gt;a pull request&lt;/a&gt; which unfortunately is unlikely to ever get merged, as the original author hasn't touched the project in years, but if any of you want the nifty new feature applying the patch and building your own custom &lt;code&gt;cpulimit&lt;/code&gt; is pretty easy.&lt;/p&gt;

&lt;p&gt;How the patch works is simple. It installs signal handlers for &lt;code&gt;SIGUSR1&lt;/code&gt; and &lt;code&gt;SIGUSR2&lt;/code&gt; which respectively increase and decrease the CPU allocation by 1%. Want to turn it up by 50%? Just write a little shell loop to send the signal 50 times:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 50&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-SIGUSR1&lt;/span&gt; &lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Determining which process to send the signal to is a bit tricky, as there are &lt;em&gt;two&lt;/em&gt; &lt;code&gt;cpulimit&lt;/code&gt; processes running. There's the first one, which is just waiting in the background for &lt;code&gt;somecommand&lt;/code&gt; to finish, then there's the watchdog that got forked off. It's the watchdog you want to send the signals to. You can tell which is the watchdog as it will generally have a higher PID and be using a little bit of CPU. If you are &lt;code&gt;cpulimit&lt;/code&gt;ing multiple processes then you can tell which watchdog is related to which process because the watchdog will have the command and its arguments on its command line. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ps aux|grep ffmpeg|grep -v grep
david   90311 103.3  0.7 36105472 485448 s011  T     6:42pm   1:07.89 ffmpeg ...
david   90312   5.6  0.0 34221044    828 s011  S     6:42pm   0:02.82 cpulimit -l 100 ffmpeg ...
david   90310   0.0  0.0 34122740    796 s011  S     6:42pm   0:00.01 cpulimit -l 100 ffmpeg ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see here that I asked &lt;code&gt;cpulimit&lt;/code&gt; to allow &lt;code&gt;ffmpeg&lt;/code&gt; to only use 1 CPU of the several available on this machine (ie to use 100% of a CPU - on modern machines the maximum allowed is the number of CPU cores * 100%). My shell accordingly started process 90310 which forked and execed &lt;code&gt;ffmpeg&lt;/code&gt; with pid 90311 and forked the watchdog process as pid 90312. The watchdog is using a little bit of CPU. It is therefore to process 90312 that I should send signals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ for i in $(seq 1 400); do kill -SIGUSR1 90312; done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will send the "turn it up a bit" signal 400 times, so &lt;code&gt;ffmpeg&lt;/code&gt; is now limited to at most 500% of a CPU, and a few moments later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ps aux|grep ffmpeg|grep -v grep
david   90311 497.0  0.8 36105472 507160 s011  T     6:42pm  12:42.37 ffmpeg ...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can see that &lt;code&gt;ffmpeg&lt;/code&gt; is running a lot harder, now taking just under 500% of the CPU.&lt;/p&gt;

</description>
      <category>c</category>
      <category>patching</category>
      <category>signals</category>
    </item>
    <item>
      <title>TIL: diff-so-fancy; and some funky git config</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Wed, 25 Jan 2023 19:12:13 +0000</pubDate>
      <link>https://forem.com/drhyde/til-diff-so-fancy-and-some-funky-git-config-2a2o</link>
      <guid>https://forem.com/drhyde/til-diff-so-fancy-and-some-funky-git-config-2a2o</guid>
      <description>&lt;p&gt;I just discovered &lt;a href="https://github.com/so-fancy/diff-so-fancy" rel="noopener noreferrer"&gt;&lt;code&gt;diff-so-fancy&lt;/code&gt;&lt;/a&gt;, and very nice it is too. I immediately added it to my standard git config, which is semi-automatically installed on every machine I use. However, I've not (yet) installed &lt;code&gt;diff-so-fancy&lt;/code&gt; on all the machines I use, and for those platforms for which it's not packaged I probably won't bother installing it from source.&lt;/p&gt;

&lt;p&gt;But if I just follow the author's instructions which amount to adding this to my &lt;code&gt;~/.gitconfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[core]
    pager = "diff-so-fancy | less --tabs=4 -RFX"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then &lt;code&gt;git diff&lt;/code&gt; will break:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git diff HEAD^
diff-so-fancy | less --tabs=4 -RFX: diff-so-fancy: command not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but there's an easy fix! Whatever is in &lt;code&gt;pager&lt;/code&gt; is just shell code, so this works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[core]
    pager = "if [ ! -z \"$(which diff-so-fancy)\" ]; then diff-so-fancy | less --tabs=4 -RFX; else less; fi"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output from &lt;code&gt;git diff&lt;/code&gt; is piped into that little script. If &lt;code&gt;diff-so-fancy&lt;/code&gt; is installed (ie if &lt;code&gt;"$(which diff-so-fancy)"&lt;/code&gt; is not zero-length) then it does exactly what &lt;code&gt;diff-so-fancy&lt;/code&gt;'s author suggests. Otherwise, if &lt;code&gt;diff-so-fancy&lt;/code&gt; isn't installed, just run &lt;code&gt;less&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>A Rant</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Fri, 20 Jan 2023 17:54:56 +0000</pubDate>
      <link>https://forem.com/drhyde/a-rant-2i0c</link>
      <guid>https://forem.com/drhyde/a-rant-2i0c</guid>
      <description>&lt;p&gt;How the &lt;em&gt;hell&lt;/em&gt;, given these interests:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lvUw8Zx2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9460vhaynhf7g9bxzr8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lvUw8Zx2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9460vhaynhf7g9bxzr8t.png" alt="My tags, which don't contain any web stuff" width="96" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;is this post in any way relevant and deserving of being promoted to me on the front page of this site?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---YVzFovq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bhkk899spc5yj1thz0ev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---YVzFovq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bhkk899spc5yj1thz0ev.png" alt='A post tagged with "css", "webdev", and "html"' width="672" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And no, negative weights being treated as "anti-follows" isn't enough. There are so many topics I'm not interested in that configuring them all is just too much work.&lt;/p&gt;

</description>
      <category>rant</category>
    </item>
    <item>
      <title>Number::Phone release candidate</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Thu, 12 Jan 2023 21:52:39 +0000</pubDate>
      <link>https://forem.com/drhyde/numberphone-release-candidate-3h7</link>
      <guid>https://forem.com/drhyde/numberphone-release-candidate-3h7</guid>
      <description>&lt;p&gt;I've recently tackled a feature request that's been sitting in the backlog for several years, to use libphonenumber's data to validate numbers with non-geographic country codes. That's codes like +800 (international freephone), +870 (Inmarsat) and so on.&lt;/p&gt;

&lt;p&gt;Previous versions would sort of work with these numbers, in that they could be instantiated as objects, but there was no information on validity or any other properties.&lt;/p&gt;

&lt;p&gt;For example here it's looking at an Iridium number. First with the previous release:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Number::Phone-&amp;gt;new("+881 672520333333333333333732")-&amp;gt;format()&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;+8816 72520333333333333333732&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Number::Phone-&amp;gt;new("+881 672520732")-&amp;gt;is_mobile()&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;undef (ie, dunno)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and with the release candidate:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Number::Phone-&amp;gt;new("+881 672520333333333333333732")&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;undef (ie, not valid)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Number::Phone-&amp;gt;new("+881 672520732")-&amp;gt;format()&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;+881 6 725 20732&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Number::Phone-&amp;gt;new("+881 672520732")-&amp;gt;is_mobile()&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Much of the code is auto-generated, and this involved quite a bit of hacking around in the build system and object instantiation, so there's the potential for exciting new bugs even for numbers in boring old geographic country codes, although I didn't have to change any of the existing tests so I am hopeful that I avoided that. &lt;a href="https://github.com/DrHyde/perl-modules-Number-Phone/pull/114/files"&gt;Here are the changes&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;I'd be grateful if you could test this, and assuming that no-one finds any bugs it will be in either the March or June quarterly release. You can download it from &lt;a href="https://cpan.metacpan.org/authors/id/D/DC/DCANTRELL/Number-Phone-3.8099_01.tar.gz"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>perl</category>
      <category>telecoms</category>
      <category>testing</category>
    </item>
    <item>
      <title>Automatic cross-platform testing: part 6: 32 bit Linux</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Sun, 17 Jul 2022 17:25:26 +0000</pubDate>
      <link>https://forem.com/drhyde/automatic-cross-platform-testing-part-6-32-bit-linux-fh2</link>
      <guid>https://forem.com/drhyde/automatic-cross-platform-testing-part-6-32-bit-linux-fh2</guid>
      <description>&lt;h1&gt;
  
  
  Previously on this channel
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/drhyde/automatic-cross-platform-testing-part-1-linux-40ih"&gt;Introduction and testing on Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/drhyde/automatic-cross-platform-testing-part-2-freebsd-2394"&gt;Testing on FreeBSD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/drhyde/automatic-cross-platform-testing-part-3-macos-2h2i"&gt;Testing on MacOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/drhyde/automatic-cross-platform-testing-part-4-windows-1p7e"&gt;Testing on Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/drhyde/automatic-cross-platform-testing-part-5-openbsd-40gg"&gt;Testing on OpenBSD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Testing on 32 bit Linux
&lt;/h1&gt;

&lt;p&gt;Just about every virtual machine out there in the various CI platforms and hosting companies is a 64 bit x86 machine. But I wanted to test my code on a 32 bit machine as well. Unfortunately without having real 32 bit hardware available, or emulating it on more modern hardware, you can't &lt;em&gt;quite&lt;/em&gt; do this, but you can at least run an OS and use libraries compiled for 32 bit hardware, 32 bit memory management, and with 32 bit pointers.&lt;/p&gt;

&lt;p&gt;It's surprisingly easy, and lot less hackish than the solution I found for testing on OpenBSD.&lt;/p&gt;

&lt;p&gt;Github workflows normally run directly on whatever host type you specify in the &lt;code&gt;runs-on&lt;/code&gt; option. But you can instead tell them to &lt;a href="https://docs.github.com/en/actions/using-jobs/running-jobs-in-a-container"&gt;run in a &lt;code&gt;container&lt;/code&gt;&lt;/a&gt;, which can be anything available on Docker Hub. Jobs running in a container can have all the same steps that you would have in a non-containerized job, including using other Github workflow actions.&lt;/p&gt;

&lt;p&gt;To test my code on 32-bit Linux I use the &lt;code&gt;i386/ubuntu:latest&lt;/code&gt; image. I am also using the same technique to run tests for one of my projects, which is sensitive to exactly which Linux distribution you are using, on Arch Linux, using the &lt;code&gt;archlinux:latest&lt;/code&gt; image.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>githubactions</category>
      <category>ci</category>
      <category>32bit</category>
    </item>
    <item>
      <title>Virtual machine efficiency</title>
      <dc:creator>David Cantrell</dc:creator>
      <pubDate>Wed, 23 Feb 2022 21:27:22 +0000</pubDate>
      <link>https://forem.com/drhyde/virtual-machine-efficiency-in1</link>
      <guid>https://forem.com/drhyde/virtual-machine-efficiency-in1</guid>
      <description>&lt;p&gt;I'm one of the &lt;a href="https://www.cpantesters.org/" rel="noopener noreferrer"&gt;CPAN testers&lt;/a&gt;, a bunch of people who test everything that gets uploaded to the CPAN. We test with multiple different builds of perl on multiple operating systems and different hardware platforms, and send reports of failing tests. When I'm wearing my Author Hat as opposed to my Tester Hat I very much appreciate test failure reports from platforms I don't have access to, or which are generated using perl builds with features I hadn't considered. I test on 5 different operating systems (6 if you count 32 bit x86 and 64 bit amd64 Linux separately) and 40 different builds of perl.&lt;/p&gt;

&lt;p&gt;I mostly test in virtual machines, running on a single Intel Mac Mini. Using VMs means I don't need multiple noisy real machines of course, but it also makes them all easier to manage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipwdai3wxrfn3fbvh1h6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipwdai3wxrfn3fbvh1h6.png" alt="10 VMs running in my dock, and if one VM should accidentally crash there'd be ..."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Until very recently I used &lt;a href="https://www.virtualbox.org/" rel="noopener noreferrer"&gt;Virtualbox&lt;/a&gt; for this, mostly because back when I started running VMs on a Mac it was the best free game in town. But it's not the most efficient and I have now switched to the much better, and still free, &lt;a href="https://mac.getutm.app/" rel="noopener noreferrer"&gt;UTM&lt;/a&gt;, which is a simple GUI wrapper around the notoriously hard to configure &lt;a href="https://www.qemu.org/" rel="noopener noreferrer"&gt;QEMU&lt;/a&gt;, patched to use Apple's hypervisor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpv9euupoipvkipdxut2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpv9euupoipvkipdxut2.png" alt="CPU usage log for a month, consistently around 50% for the first half, short spikes of 100% and intervening periods of much lower usage after that"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This chart shows CPU usage over the last month. The first half is when I was using Virtualbox. Running all those test builds usage was a fairly constant 50% of what was available. The VMs could keep up with the workload, but there was little capacity for more despite all that unused CPU. No matter how many CPUs I allocated to the various VMs, it just wouldn't use all the resources. It's very clear in the chart when I started migrating VMs from Virtualbox to UTM. CPU usage starts spiking much higher - indeed, it reaches 100% and stays there when the VMs test a batch of uploads - but then drops to almost zero when that batch has finished. The work is done faster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u8vuaalasvtrstiros1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u8vuaalasvtrstiros1.png" alt="Memory usage log for a month, very high at the start then dropping off a cliff"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Virtualbox is also a memory hog. This chart shows that I couldn't really add any more Virtualbox VMs, as there was little memory left. And worse, most of what was in use (the blue section of the chart) is "wired" memory, in kernel-space. Yes, most of the memory Virtualbox was using was being used by its kernel extension. It is again clear when I started migrating VMs to UTM. Memory usage falls off a cliff, and &lt;em&gt;"wired"&lt;/em&gt; memory usage falls even faster, with a far greater proportion of the lower total being just normal user-space memory (the red section of the chart).&lt;/p&gt;

&lt;p&gt;Now that I've got back all that wasted memory I am, of course, beginning to fill it with more VMs, this time for development as opposed to testing. I have recently become a larval stage &lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;Rustacean&lt;/a&gt; and have dived headlong into reporting &lt;a href="https://github.com/dylni/process_control/issues/12" rel="noopener noreferrer"&gt;obscure platform-specific bugs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But you can see from my dock that I've still got three Virtualbox VMs running. Why?&lt;/p&gt;

&lt;p&gt;While UTM is much better at resource usage, it's not perfect. Virtualbox seems to emulate more of a system instead of passing it through to the hypervisor and so is better for running more unusual OSes. I have two VMs there running &lt;a href="https://omnios.org/" rel="noopener noreferrer"&gt;Illumos&lt;/a&gt; which I have yet to figure out how to boot in UTM. A problem that I think is related to UTM's greater use of the hypervisor is that you can't suspend and resume VMs that use it instead of being emulated, so I've still got a few VMs hanging around in Virtualbox which spend most of their time suspended. Finally, what stops me from using UTM at work is that you &lt;a href="https://github.com/hashicorp/vagrant/issues/12518" rel="noopener noreferrer"&gt;can't use it as a Vagrant provider&lt;/a&gt;. This is incredibly annoying, as the lack of a decent virtualization application makes the otherwise very nice M1 Macs nothing more than pretty toys. I expect that this glaring lack will be fixed within the next couple of years.&lt;/p&gt;

</description>
      <category>virtualization</category>
      <category>virtualbox</category>
      <category>utm</category>
      <category>macos</category>
    </item>
  </channel>
</rss>
