<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Dave Vandyke, Programmer, Bristol UK - Blog</title>
  <link href="https://kzar.co.uk/blog"/>
  <link type="application/atom+xml" rel="self" href="https://kzar.co.uk/blog/atom.xml"/>
  <updated>2026-05-13T11:40:00+01:00</updated>
  <author>
    <name>Dave Vandyke</name>
    <email>kzar@kzar.co.uk</email>
    <uri>https://kzar.co.uk</uri>
  </author>
  <id>https://kzar.co.uk/blog</id>
  <icon>https://kzar.co.uk/images/favicon.webp</icon>
  <rights>&#169; Copyright 2007 - 2026 Dave Vandyke. All rights reserved.</rights>

  <category term="chrome" />
  <category term="clojure" />
  <category term="code" />
  <category term="compojure" />
  <category term="couchdb" />
  <category term="emacs" />
  <category term="embedded" />
  <category term="firefox" />
  <category term="grunt-contrib-watch" />
  <category term="howoto" />
  <category term="howto" />
  <category term="javascript" />
  <category term="linux" />
  <category term="lisp" />
  <category term="mac" />
  <category term="news" />
  <category term="nginx" />
  <category term="php" />
  <category term="productivity" />
  <category term="project" />
  <category term="projects" />
  <category term="python" />
  <category term="rails" />
  <category term="remotework" />
  <category term="ruby" />
  <category term="security" />
  <category term="thoughts" />
  <category term="vagrant" />
  <category term="web.py" />
  <category term="webext" />
  <category term="windows" />
  <category term="wordpress" />
  
  <entry>
    <id>https://kzar.co.uk/blog/2023/10/06/how-to-write-a-good-web-browser/extension-bug-report</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2023/10/06/how-to-write-a-good-web-browser/extension-bug-report" />
    <title>How to write a good web browser/extension bug report</title>
    <published>2023-10-06T00:00:00+00:00</published>
    <updated>2023-10-06T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>So you&rsquo;ve hit a problem with your web browser or browser extension of choice,
and you&rsquo;d like to report the issue. Writing that up properly can be tricky.
It can be hard to know where to start and which details to include. I&rsquo;m someone
who&rsquo;s often on the receiving end of such bug reports, so I thought I&rsquo;d write up
a quick guide explaining what to include and why.</p>
<p>First of all, the goal of a bug report should be to make the problem clear and
to help the development team reproduce the issue<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. For that, they will need
to know what happened, what should have happened instead, the steps to take to
trigger the problem, and any relevant details about your device (e.g. browser
version). Stick to the facts, keep it polite, and take your time to think
through the details.</p>
<p>Here&rsquo;s an example of a bad bug report:</p>
<pre tabindex="0"><code>You broke Dave&#39;s website, fix it!
</code></pre><p>It&rsquo;s kind of rude, and it does not contain enough information for anyone to
understand or reproduce the issue. If I received this bug report I might try
loading my homepage in Firefox and think &ldquo;Well, it works for me&rdquo;. Unfortunately,
bug reports like this are mostly a waste of everyone&rsquo;s time.</p>
<p>Here&rsquo;s an example of a good bug report (for the same problem):</p>
<pre tabindex="0"><code># Environment
- Windows 10 (version 22H2), Edge browser (version 116.0.1938.81)
- DuckDuckGo Privacy Essentials browser extension (version 2023.9.20)

# Steps to reproduce
1. Browse to https://kzar.co.uk/blog
2. Scroll to the bottom of the page.
3. Wait a few seconds.

# What happens
- The text goes blurry and is hard to read.

# What should happen
- The text stays sharp.

# Notes
- This started happening in the past few weeks.
- The problem doesn&#39;t happen when I use Firefox.
- I also see the problem on https://testpages.kzar.co.uk/

&lt;Attached Screenshot.png&gt;
</code></pre><p>Let&rsquo;s break that down.</p>
<h3 id="environment">Environment</h3>
<p>The environment section is where you mention<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> any relevant details about how
your browser and device are set up. Browser and Operating System version numbers
are vital to include since often problems will occur with one version but not
another. It&rsquo;s also important to include a list of your enabled browser
extensions, and if possible their versions too. Some browser extensions clash
with one another, or clash with certain websites, so we need to know which
browser extensions you are using. Often browser extensions are the root cause of
the issue. Another important detail (though not shown above), is to list any
advanced tracking protection and similar features that are enabled in your
browser. For example, if you have Firefox&rsquo;s &ldquo;Enhanced Tracking Protection&rdquo;
feature enabled for the website (or you&rsquo;ve disabled it), it&rsquo;s well worth
mentioning that. If you have an ad blocker and you&rsquo;ve added custom filters or
filter list subscriptions, list those too.</p>
<p>If you&rsquo;re not sure about some of these details, that&rsquo;s OK. Just try your best.
Anything you <em>can</em> include here will save a lot of time, my first response is
often to ask for these details.</p>
<h3 id="steps-to-reproduce">Steps to reproduce</h3>
<p>These are the specific steps someone needs to take to trigger the problem. If
the bug is breaking a website, start with the full website address of the broken
page and go from there. If the problem happens on multiple pages, just include
one example here. Again, the goal is to help the development team reproduce the
issue, not to include an exhaustive list of all the ways it can be triggered.</p>
<p>Carefully think through what all the steps are. One good tip is to try
triggering the problem by following your steps in a fresh private/incognito tab.
It&rsquo;s important not to miss anything. Mention it if you need to log in to the
website or change the website&rsquo;s settings for the problem to occur. If the
problem doesn&rsquo;t happen immediately, roughly how long do you need to wait? If
the problem doesn&rsquo;t happen every time, mention that and which steps might need
to be retried.</p>
<h3 id="what-happens">What happens</h3>
<p>Clearly state what the problem is. &ldquo;It&rsquo;s broken&rdquo; is not enough, but &ldquo;The page
starts to load, but then goes blank&rdquo; is.</p>
<h3 id="what-should-happen">What should happen</h3>
<p>Clearly state what should have happened instead. This one might seem silly, but
often it really does help. The person reviewing this bug report might have never
used the website before, so they might not understand how the website is
supposed to work, or what <em>you</em> expected to happen. By explaining here you help
them understand. Something as simple as &ldquo;The website finishes loading and does
not go blank.&rdquo; is fine.</p>
<h3 id="notes">Notes</h3>
<p>This is an optional section, in which you can include any other thoughts or notes
that you think might be useful. If you have a suspicion about what the cause of
the problem is, include a brief note. If you have noticed that the problem only
started recently, or only happens on Wednesdays, or doesn&rsquo;t happen in Chrome, or
when you turn off your ad blocker - add a note. If the problem happens on many
web pages, this is the time to mention it. Keep the notes short, quite often
they aren&rsquo;t as important as you might think, but it&rsquo;s still worth sharing them.</p>
<h3 id="attachments">Attachments</h3>
<p>Sometimes it&rsquo;s worth adding a file attachment to your bug report. Things to
include:</p>
<ul>
<li>A screenshot or screencast/video of the problem happening. Worth including if
you can safely do so, but <em>please</em> be careful not to share your personal
information - your bug report might be public and open to the world! Note that
including a screenshot is a bonus, but it does not replace the written
sections.</li>
<li>For more advanced bug reports, it might be necessary to attach some files
which are needed to reproduce the problem. This is not usually necessary for
most bug reports, but if for instance, you&rsquo;re a developer reporting a complex
bug it might be.</li>
</ul>
<p>Anyway, just do your best and don&rsquo;t worry if you don&rsquo;t get it perfect first try!
If the person investigating your bug asks some clarifying questions, try to
answer those as best you can. Hopefully, you&rsquo;ll be able to work together to get
the issue resolved. Be kind with your words and be patient<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, it goes a long
way and is appreciated. Thanks for taking the time to write a good bug report!</p>
<h2 id="notes-1">Notes</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>To investigate a bug, the first step is reproducing the issue. From there,
the developer can start to figure out the cause, understand what&rsquo;s going
wrong, and work to fix the problem. <em>Very</em> occasionally, developers will
work to fix an unreproducible bug. This will be in extreme circumstances,
perhaps some serious issue is being reported by lots of people but the
problem happens seemingly at random. Don&rsquo;t count on this though. Hoping
for a developer to fix an unreproducible bug is like playing the lottery
as a retirement strategy.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Some environment details like the browser version might be automatically
included by the bug reporting tool you&rsquo;re using. Unfortunately, though,
it&rsquo;s usually not obvious <em>which</em> details will be sent automatically. When
in doubt, include it yourself.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>The volume of incoming bug reports can sometimes be overwhelming and hard
to handle. We want to help you quickly, but sometimes that&rsquo;s not possible.
Forgive us if it takes a while.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2022/10/29/chrome-manifest-v3-extension-development-advice</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2022/10/29/chrome-manifest-v3-extension-development-advice" />
    <title>Chrome Manifest V3 extension development advice</title>
    <published>2022-10-29T00:00:00+00:00</published>
    <updated>2022-10-29T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Chrome&rsquo;s transition to the Manifest V3 (MV3) extension APIs is almost over. Next year all Chrome extensions will be using the new APIs<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.
Some of the differences are tricky to handle. Since I spent a bunch of time porting the <a href="https://adblockplus.org/">Adblock Plus</a> and <a href="https://chrome.google.com/webstore/detail/duckduckgo-privacy-essent/bkdgflcldnnnapblkhphbgpggdiikppg">DuckDuckGo Privacy Essentials</a> extensions to the MV3 APIs, I thought I&rsquo;d write up a few notes and tips for other extension developers. This post is not a migration guide; I recommend <a href="https://developer.chrome.com/docs/extensions/mv3/intro/">reading the official docs</a> if you&rsquo;re just getting started. Instead, I aim to share some practical advice I learned the hard way.</p>
<h2 id="declarativenetrequest-api">declarativeNetRequest API</h2>
<p>One of the largest changes with MV3 is the switch from <a href="https://developer.chrome.com/docs/extensions/reference/webRequest/">chrome.webRequest</a> to <a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/">chrome.declarativeNetRequest</a> for request blocking and redirection. The APIs work in a fundamentally different way. Instead of an <a href="https://developer.chrome.com/docs/extensions/reference/webRequest/#event-onBeforeRequest">onBeforeRequest</a> event firing for each request which lets the extension decide ad-hoc what action to take, the extension must provide Chrome with declarativeNetRequest rules upfront. The browser is then in control of enforcing those rules, so the extension no longer gets to decide on a case-by-case basis what happens to requests.</p>
<p>There are two main types of declarativeNetRequest rules. Static rules, which are bundled with the extension at build time in JSON ruleset files, and dynamic/session rules that the extension can add or remove dynamically at run time. In other words, extensions can add or remove individual dynamic/session rules at will but not individual static rules. Extensions can only disable or enable whole static rulesets at runtime.</p>
<p><a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#property">There are lots of different restrictions</a>. For example, an extension can only have up to 5,000 dynamic/session rules at one time but
up to 30,000 - 330,000 static rules enabled at one time. An extension can have up to 50 static rulesets, but only ten can be enabled
at any time. Often, the choice between static and dynamic rules is made for you by those restrictions. If you&rsquo;re unsure which type of rule to use, start
by figuring out how many rules your extension will need to function - you can work backwards from there.</p>
<p>Chrome parses dynamic and session rules when the extension adds them (when the extension calls the <a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#method-updateDynamicRules">declarativeNetRequest.updateDynamicRules</a> or
<a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#method-updateSessionRules">declarativeNetRequest.updateSessionRules</a> APIs). If there are parsing errors, Chrome surfaces them immediately. Chrome handles static rulesets differently. An extension&rsquo;s bundled static rulesets can potentially contain many more rules than could ever be enabled at one time<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, so parsing them all could be
very expensive (aka slow). Therefore Chrome only parses static rulesets when they are enabled. The problem is that rule errors cannot be
found in advance since the rules aren&rsquo;t parsed in advance! Worse, most errors in static rules result in the invalid rule being
silently dropped - good for the user, but not so helpful during development! But wait, there <em>is</em> hope. Chrome parses all static rulesets at install time for unpacked extensions and surfaces any errors found. That results in a slower extension installation but helps developers spot any problems in their static rulesets. So when developing an extension with static rulesets, remember to test installing the extension unpacked (<code>chrome://extensions</code> -&gt; &ldquo;Developer mode&rdquo; -&gt; &ldquo;Load unpacked&rdquo;) and then check for errors in the extension listing. That way, you can be sure
your static rules are valid. If there are static rule errors, check again after fixing them - only the first N errors
are displayed.</p>
<figure>
  <img src="/images/blog/static-ruleset-error.webp"
       alt="Screenshot of an example static rule error.">
  <figcaption>Static rule error, reported for an unpacked extension.</figcaption>
</figure>
<p>While reasoning about how <a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#matching-algorithm">one or two declarativeNetRequest rules will match</a> is simple enough, it soon gets tricky when you&rsquo;re working with rulesets containing thousands
of rules. Questions like &ldquo;Which rule matches this request?&rdquo; or &ldquo;What even should happen here?!&rdquo; get confusing to answer fast.
That&rsquo;s why I added the <a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#method-testMatchOutcome">declarativeNetRequest.testMatchOutcome</a> API. It&rsquo;s a testing function that you can use to quickly check
which enabled rules (if any) match a hypothetical request. You can use it manually from the extension&rsquo;s background ServiceWorker (for unpacked extensions), but you can also use it in automated tests via <a href="https://pptr.dev/">Puppeteer</a>. If you&rsquo;d like an example, see the <a href="https://github.com/kzar/abp2dnr/blob/main/test/requestMatching.js">abp2dnr request matching tests</a> and the corresponding
<a href="https://github.com/duckduckgo/ddg2dnr/blob/main/puppeteerInterface.js">Puppeteer interface code</a>. Having unit tests that not only confirm your code is generating the correct rules but also that those rules will match requests as you intended is invaluable.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Example from the abp2dnr request matching tests.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>assert.deepEqual(
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">await</span> testRequestOutcome(<span style="color:#008000;font-weight:bold">this</span>.browser, {
</span></span><span style="display:flex;"><span>    url<span style="color:#666">:</span> <span style="color:#ba2121">&#34;https://example.invalid/advert&#34;</span>,
</span></span><span style="display:flex;"><span>    type<span style="color:#666">:</span> <span style="color:#ba2121">&#34;image&#34;</span>
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;block&#34;</span>
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>When writing rules, the <a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#property-RuleCondition-urlFilter">urlFilter</a> or <a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#property-RuleCondition-regexFilter">regexFilter</a> conditions can be used to restrict matches to a
particular website. One tip is that <a href="https://groups.google.com/a/chromium.org/g/chromium-extensions/c/4971ZS9cI7E/m/jnUfCtGUAQAJ">since I added the requestDomains/excludedRequestDomains conditions</a>, domain conditions can often be used instead. The advantage of domain
conditions (apart from being more efficient to match<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>) is that a single domain condition can list multiple domains to match (or to exclude). Sometimes it&rsquo;s possible
to combine rules that have different urlFilter conditions - but that are otherwise the same - by using a suitable
requestDomains condition. That can have a major impact. For example, it led to a two thirds<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> reduction in the number of declarativeNetRequest rules required for <a href="https://easylist.to/">EasyList</a>!</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Two similar rules, with a different urlFilter...
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>[
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    id<span style="color:#666">:</span> <span style="color:#666">1</span>,
</span></span><span style="display:flex;"><span>    action<span style="color:#666">:</span> { type<span style="color:#666">:</span> <span style="color:#ba2121">&#34;block&#34;</span> },
</span></span><span style="display:flex;"><span>    condition<span style="color:#666">:</span> {
</span></span><span style="display:flex;"><span>      urlFilter<span style="color:#666">:</span> <span style="color:#ba2121">&#34;||domain1.example*/ad.js&#34;</span>,
</span></span><span style="display:flex;"><span>      resourceTypes<span style="color:#666">:</span> [<span style="color:#ba2121">&#34;script&#34;</span>]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    id<span style="color:#666">:</span> <span style="color:#666">2</span>,
</span></span><span style="display:flex;"><span>    action<span style="color:#666">:</span> { type<span style="color:#666">:</span> <span style="color:#ba2121">&#34;block&#34;</span> },
</span></span><span style="display:flex;"><span>    condition<span style="color:#666">:</span> {
</span></span><span style="display:flex;"><span>      urlFilter<span style="color:#666">:</span> <span style="color:#ba2121">&#34;||domain2.example*/ad.js&#34;</span>,
</span></span><span style="display:flex;"><span>      resourceTypes<span style="color:#666">:</span> [<span style="color:#ba2121">&#34;script&#34;</span>]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// ...can be combined into one rule like so:
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>[
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    id<span style="color:#666">:</span> <span style="color:#666">1</span>,
</span></span><span style="display:flex;"><span>    action<span style="color:#666">:</span> { type<span style="color:#666">:</span> <span style="color:#ba2121">&#34;block&#34;</span> },
</span></span><span style="display:flex;"><span>    condition<span style="color:#666">:</span> {
</span></span><span style="display:flex;"><span>      urlFilter<span style="color:#666">:</span> <span style="color:#ba2121">&#34;/ad.js&#34;</span>,
</span></span><span style="display:flex;"><span>      resourceTypes<span style="color:#666">:</span> [<span style="color:#ba2121">&#34;script&#34;</span>],
</span></span><span style="display:flex;"><span>      requestDomains<span style="color:#666">:</span> [<span style="color:#ba2121">&#34;domain1.example&#34;</span>, <span style="color:#ba2121">&#34;domain2.example&#34;</span>]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>When using dynamic/session rules, I found that it&rsquo;s often necessary to maintain a data structure that maps declarativeNetRequest
rules to their corresponding representation in the extension. For example, for Adblock Plus, we needed to know which declarativeNetRequest
rule(s) corresponded to which Adblock Plus filters. That way, we could remove the correct declarativeNetRequest rules when one of the filter lists removed a filter.</p>
<p>You can store the mapping data structure in session storage (<a href="https://developer.chrome.com/docs/extensions/reference/storage/#property-session">chrome.storage.session</a>) or
local storage (<a href="https://developer.chrome.com/docs/extensions/reference/storage/#property-local">chrome.storage.local</a>), depending on if you&rsquo;re working with session or dynamic rules. The problem is, how can you keep the data structure in sync with the active declarativeNetRequest rules? Suppose you add some dynamic rules and then update your mapping data structure in local storage to note the new rules&hellip; what happens if one of those operations fails? What happens if there is a power cut mid-update?</p>
<p>That is why I recommend ensuring your dynamic/session declarativeNetRequest rules are in sync with the extension&rsquo;s state. You can do that by
storing a state ID (e.g. a UUID or random number) in both your mapping data structure and with your declarativeNetRequest rules. Update that state ID every time
you make declarativeNetRequest rule changes. If the state IDs diverge, you&rsquo;ll know to rebuild all of the declarativeNetRequest rules to reflect the extension&rsquo;s state.
Storing a state ID in your mapping data structure is easy enough, but how do you store it with the declarativeNetRequest rules? One way is to create a special
declarativeNetRequest rule with a known rule ID (e.g. 1) that contains the state ID as a part of its urlFilter condition. It&rsquo;s worth ensuring the urlFilter will never
match actual requests, so prefix it with an invalid domain. You can then read the rule back out of the declarativeNetRequest API and check the state ID against what you expect.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Example state ID declarativeNetRequest rule.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>[
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    id<span style="color:#666">:</span> <span style="color:#666">1</span>,
</span></span><span style="display:flex;"><span>    action<span style="color:#666">:</span> { type<span style="color:#666">:</span> <span style="color:#ba2121">&#34;block&#34;</span> },
</span></span><span style="display:flex;"><span>    condition<span style="color:#666">:</span> {
</span></span><span style="display:flex;"><span>      urlFilter<span style="color:#666">:</span> <span style="color:#ba2121">&#34;||state-id.invalid/8f084278-760a-4ee7-b235-4753e4b3210f&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>Sometimes maintaining a mapping data structure for your rules is overkill. If your use case is simple enough that pre-determined rule IDs or rule ID ranges are sufficient, then, by all means, skip the above!</p>
<p>Lastly, if you&rsquo;d like some declarativeNetRequest inspiration, both the Adblock Plus (<a href="https://github.com/kzar/abp2dnr">abp2dnr</a>)
and DuckDuckGo (<a href="https://github.com/kzar/ddg2dnr">ddg2dnr</a>) ruleset generation libraries are Open Source and available on GitHub.</p>
<h2 id="background-serviceworker-and-extension-state">Background ServiceWorker and extension state</h2>
<p>I touched on this above, but a pain point when migrating to the MV3 APIs is the switch to background ServiceWorkers and state
management. With the old Chrome Manifest V2 (MV2) APIs, extensions have an invisible background &ldquo;page&rdquo; that Chrome creates when the
extension starts and keeps running until the user disables the extension or closes the browser entirely<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>. That meant you could, for example, process a bunch of
data once when the extension started and then refer to the processed data later when you needed it. It also meant that it was
&ldquo;safe&rdquo; to store the extension&rsquo;s state in local variables - so long as the state didn&rsquo;t need to persist through extension restart.
In particular, this meant that an event listener could write some state to a local variable, ready for the state to be read back in
the future from another event listener.</p>
<p>That has all changed with MV3 and the switch to background ServiceWorkers. Chrome can kill or restart the background ServiceWorker at any point, often
after only seconds. State in local variables is lost when that happens. Data needs to be reprocessed. State shared
between event listeners is lost. This poses problems for extension
developers migrating their extensions to MV3, and I recommend checking out the official <a href="https://developer.chrome.com/docs/extensions/mv3/migrating_to_service_workers/">background ServiceWorker migration guide</a>
before anything else. But here are some tricks:</p>
<p>Chrome can restart an extension&rsquo;s background ServiceWorker at any time. In theory, you have no control over that, but in practice, there are
some things you can do. Most importantly, you can keep the background ServiceWorker running for around five minutes<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> by <a href="https://developer.chrome.com/docs/extensions/mv3/messaging/#connect">opening long-lived messaging
connections</a>. If your extension has a content script that needs to send messages to the background
anyway, I recommend using long-lived connections (aka <a href="https://developer.chrome.com/docs/extensions/reference/runtime/#method-connect">chrome.runtime.connect</a>) instead of the simpler messaging API (aka
<a href="https://developer.chrome.com/docs/extensions/reference/runtime/#method-sendMessage">chrome.runtime.sendMessage</a>). It&rsquo;s more efficient if you&rsquo;re sending a lot of data, plus it will keep your background ServiceWorker running while the website is open and the content script is running. The content script can also listen for <a href="https://developer.chrome.com/docs/extensions/reference/runtime/#event-Port-onDisconnect">the disconnect event</a> of the long-lived messaging
connection, which will fire when Chrome kills the background ServiceWorker. At that point, the content script can re-open the connection, ensuring the background ServiceWorker is restarted in the process - and the clock starts again.</p>
<p>Long-lived messaging connections are generally superior to the simpler one-time messaging API, but they are less ergonomic to use. That&rsquo;s why I created the
tiny <a href="https://github.com/kzar/webext-messaging">webext-messaging library</a>. It provides a much simpler way to do
extension messaging, and you get long-lived connections (and the above advantages) for free.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Example webext-messaging use from a content script.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span><span style="color:#008000;font-weight:bold">import</span> { addConnection, dispatch, port } from <span style="color:#ba2121">&#34;webext-messaging&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Start listening for incoming messages and set up a long-lived connection.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>chrome.runtime.onMessage.addListener(dispatch);
</span></span><span style="display:flex;"><span>addConnection(chrome.runtime.connect());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Send a random number to the background through the long-lived messaging
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// connection.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>port.post(<span style="color:#ba2121">&#34;randomNumber&#34;</span>, { randomNumber<span style="color:#666">:</span> <span style="color:#008000">Math</span>.random() });
</span></span></code></pre></div><p>There&rsquo;s one other thing to note about the background ServiceWorker&rsquo;s lifespan. While you&rsquo;re inspecting the background ServiceWorker (e.g. have the background console open), the ServiceWorker will persist much longer (possibly forever). That makes
testing the background ServiceWorker lifespan tricky since you can&rsquo;t view errors as they show up in the console until <em>after</em> the ServiceWorker was restarted, but
by then, the errors are lost! What can you do? Well, instead of using the debugger or normal logging, use <a href="https://developer.mozilla.org/en-US/docs/Web/API/console/error">console.error</a>. Take care to include the current time with logged errors and to keep them short. Then, if you click the &ldquo;Errors&rdquo; button
on the <code>chrome://extensions</code> page for your extension, you&rsquo;ll get a list of your recently logged errors. That&rsquo;s enough to
figure out what&rsquo;s going on without using the usual developer tools. But note if you log too many errors, the older ones are lost. So keep
noting them down as they come in, or take care not to log too many.</p>
<figure>
  <img src="/images/blog/manually-logged-errors.webp"
       alt="Screenshot of manually logged errors.">
  <figcaption>Manually logged errors that persist through background ServiceWorker restart.</figcaption>
</figure>
<p>When Chrome starts an extension&rsquo;s background ServiceWorker, if the background ServiceWorker throws an exception, Chrome will give up, and the background ServiceWorker will become inactive. That can make debugging the cause of the exception hard. When that happens, I usually remove all code from the background script, restart the extension, open the background console, and then paste the code into the console. Chrome will pause as the code is parsed and run, but after that, it will display details about what caused the exception in the console.</p>
<p>Finally, be extra careful when using any APIs that persist state in their own way. For example, if you naively <a href="https://developer.chrome.com/docs/extensions/reference/contextMenus/#method-create">add a context menu item</a> when the background ServiceWorker first starts, the background ServiceWorker will attempt to re-add that context menu item every time it restarts. Another pernicious example is <a href="https://developer.chrome.com/docs/extensions/reference/alarms/">the chrome.alarms API</a>. Check that alarms don&rsquo;t already exist before
adding them. Otherwise, if you set an alarm for five minutes time, the background ServiceWorker will restart before it fires.
At that point, the alarm is recreated (no errors) for five minutes time again. This repeats ad infinitum, and five minutes never comes! <a href="https://stackoverflow.com/a/66391601/1226469">See wOxxOm&rsquo;s StackOverflow post</a> on the topic.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">/**
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> * Add a context menu item, ignoring the error if it already exists.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> * @param {Object} createProperties
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> *   See https://developer.chrome.com/docs/extensions/reference/contextMenus/#type-create-createProperties
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> * @returns {Promise}
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> */</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">function</span> createContextMenu(createProperties) {
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">return</span> <span style="color:#008000;font-weight:bold">new</span> <span style="color:#008000">Promise</span>((resolve, reject) =&gt; {
</span></span><span style="display:flex;"><span>      chrome.contextMenus.create(createProperties, () =&gt; {
</span></span><span style="display:flex;"><span>        <span style="color:#008000;font-weight:bold">const</span> lastError <span style="color:#666">=</span> chrome.runtime.lastError
</span></span><span style="display:flex;"><span>        <span style="color:#008000;font-weight:bold">if</span> (lastError <span style="color:#666">&amp;&amp;</span> lastError.message <span style="color:#666">&amp;&amp;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">!</span>lastError.message.startsWith(<span style="color:#ba2121">&#34;Cannot create item with duplicate id&#34;</span>)) {
</span></span><span style="display:flex;"><span>          reject(lastError);
</span></span><span style="display:flex;"><span>        } <span style="color:#008000;font-weight:bold">else</span> {
</span></span><span style="display:flex;"><span>          resolve();
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">/**
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> * Create an alarm, but only if it doesn&#39;t already exist.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> * @param {string} name
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> * @param {Object} alarmCreateInfo
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> *   See https://developer.chrome.com/docs/extensions/reference/alarms/#type-AlarmCreateInfo
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> * @returns {Promise}
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"> */</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">async</span> <span style="color:#008000;font-weight:bold">function</span> setAlarm(name, alarmCreateInfo) {
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">const</span> existingAlarm <span style="color:#666">=</span> <span style="color:#008000;font-weight:bold">await</span> chrome.alarms.get(name);
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#666">!</span>existingAlarm)
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">return</span> <span style="color:#008000;font-weight:bold">await</span> chrome.alarms.create(name, alarmCreateInfo);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="learn-more">Learn more</h2>
<ul>
<li><a href="https://developer.chrome.com/docs/extensions/mv3/intro/">Official introduction to Chrome MV3</a></li>
<li><a href="https://developer.chrome.com/docs/extensions/mv3/mv3-migration/">The official Chrome MV3 migration guide</a></li>
<li><a href="https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest">declarativeNetRequest API docs</a></li>
<li><a href="https://stackoverflow.com/search?q=user:3959875+%5Bgoogle-chrome-extension%5D">The venerable wOxxOm&rsquo;s posts on StackOverflow</a></li>
<li><a href="https://groups.google.com/u/1/a/chromium.org/g/chromium-extensions">The chrome-extensions Google group</a> (including a <a href="https://groups.google.com/a/chromium.org/g/chromium-extensions/c/f5gWcNchXYw/m/srqAw7LvBAAJ">discussion about this post</a>)</li>
<li><a href="https://crbug.com/?q=label%3ADeclarativeNetRequest&amp;can=1">declarativeNetRequest Chromium issues</a></li>
<li><a href="https://source.chromium.org/chromium/chromium/src/+/main:extensions/browser/api/declarative_net_request/declarative_net_request_api.cc?q=declarativeNetRequest&amp;ss=chromium%2Fchromium%2Fsrc">The declarativeNetRequest Chromium code</a></li>
<li>Mentioned projects: <a href="https://github.com/kzar/abp2dnr">abp2dnr</a>, <a href="https://github.com/kzar/ddg2dnr">ddg2dnr</a>, and <a href="https://github.com/kzar/webext-messaging">webext-messaging</a>.</li>
</ul>
<h2 id="notes">Notes</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Though as the joke goes, &ldquo;Chrome MV3 is always six months away&rdquo;&hellip;&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>From memory, the theoretical limit is 50 x 330,000, so &gt; 16 million rules!&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://source.chromium.org/chromium/chromium/src/+/main:components/url_pattern_index/url_pattern_index.cc;l=566-619">The domain-matching algorithm</a> is cool. First off, domain conditions are sorted when the ruleset is indexed. The sort order is first by domain length, then alphabetical. During matching, for short lists of domains (&lt;= 5), each domain is just checked in turn. But for longer lists of domains, a <a href="https://en.wikipedia.org/wiki/Binary_search_algorithm">binary search</a> is used. Domains longer than the desired domain can be quickly skipped past thanks to the sort order. If the binary search for a domain fails, its first subdomain is removed from the domain, and the search is repeated. When the search is repeated, the left-bound can start from its previous position since we know that a domain minus a subdomain is always shorter than the original domain. Searches are repeated in this way until either a match is found or all subdomains have been removed.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://github.com/kzar/abp2dnr/commit/c66a468b379b6f5a556e3a3a70cd2118306e6dec">At the time</a>,
that was a reduction from 28,191 down to 9,592 declarativeNetRequest rules!&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Well, that&rsquo;s a simplification. There are some other times when the
background page is restarted, for example, when the extension is
updated&hellip; but you get the idea.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>That&rsquo;s an undocumented implementation detail of the browser and
is subject to change. Nevertheless, it&rsquo;s worth considering. Opening long-lived messaging connections in this way can
reduce the number of background ServiceWorker restarts by an order of magnitude. If you&rsquo;re
interested, we discussed this in more detail in <a href="https://crbug.com/1146434#c6">the comments on issue 1146434</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2022/08/16/setting-up-a-simple-audiobook-feed-on-a-synology-nas</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2022/08/16/setting-up-a-simple-audiobook-feed-on-a-synology-nas" />
    <title>Setting up a simple audiobook feed on a Synology NAS</title>
    <published>2022-08-16T00:00:00+00:00</published>
    <updated>2022-08-16T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I&rsquo;ve been listening to a lot of audiobooks and podcasts recently. I like the Open Source <a href="https://antennapod.org/">AntennaPod</a> Android app, so I wanted to listen to my audiobooks using that along with my podcast subscriptions. To start with, I copied the audiobooks onto my phone with a USB cable and added those in AntennaPod as a &ldquo;local folder&rdquo; subscription, but that wasn&rsquo;t very convenient!</p>
<p>The dream was to have a proper audiobook subscription that would stay up to date as I copied books onto my NAS. That way, I could quickly use the app to download, queue and listen to books as I do with podcast episodes.</p>
<figure>
  <img src="/images/blog/audiobooks-feed.webp"
       alt="Screenshot of audiobooks feed displayed by AntennaPod.">
  <figcaption>Audiobooks feed displayed by AntennaPod.</figcaption>
</figure>
<p>I have a Synology DS918+ NAS. It&rsquo;s set up to sleep when it has been inactive for a while to save power. While I wanted a convenient way to listen to audiobooks, I needed to avoid installing large packages or Docker images that could prevent the system from sleeping. Better to have a small PHP script to generate the audiobook RSS feed instead. That way, I could use Synology&rsquo;s built-in web server.</p>
<p>I looked for existing PHP scripts and found a couple (e.g. <a href="https://gist.github.com/vsoch/4898025919365bf23b6f">here</a> and <a href="https://gist.github.com/greg-randall/650ae7566f760da1ff702abfc09ed020">here</a>), but none worked quite how I wanted. I missed preview images, book descriptions and other details. It seemed a shame, especially since that was all available in the ID3 tags of the MP3 files produced by <a href="https://openaudible.org/">OpenAudible</a>.</p>
<p>So I wrote my own script. I&rsquo;ve been using it for a while now, and it works great. Here&rsquo;s how to set it up:</p>
<p>Assumptions:</p>
<ul>
<li>All audiobooks are thrown in a folder, along with an <code>audiobooks.png</code> cover image for the feed and the <code>index.php</code> script (see below).</li>
<li>All audiobooks are MP3/M4B files ending with <code>.mp3</code> or <code>.m4b</code>.</li>
<li>Most of the audiobook files include proper metadata (e.g. title, author, publish date) and cover images.</li>
<li>The files are hosted on a Synology NAS with a static IP of <code>192.168.1.100</code>.</li>
</ul>
<p>Setup instructions:</p>
<ol>
<li>Add the <a href="https://synocommunity.com/">SynoCommunity</a> Package Source in the Package Center (<a href="https://synocommunity.com/#easy-install">instructions here</a>).</li>
<li>Install the following packages in the Synology Package Center:
<ul>
<li>Web Station</li>
<li>ffmpeg</li>
<li>PHP (e.g. PHP 5.6)</li>
</ul>
</li>
<li>Open the Web Station:
<ul>
<li>Click &ldquo;General Settings&rdquo; and ensure &ldquo;HTTP back-end server&rdquo; is set to &ldquo;Nginx&rdquo; and &ldquo;PHP&rdquo; is set to the installed PHP version.</li>
<li>Click &ldquo;Virtual Host&rdquo; and then &ldquo;Create&rdquo;:
<ul>
<li>Select &ldquo;Port-based&rdquo;.</li>
<li>Check &ldquo;HTTP&rdquo; and type &ldquo;60808&rdquo; for the port.</li>
<li>Select the folder containing your audiobook files for the &ldquo;Document root&rdquo; option.</li>
<li>Ensure &ldquo;HTTP back-end server&rdquo; and &ldquo;PHP&rdquo; options are set the same as in the &ldquo;General Settings&rdquo; screen.</li>
<li>Click &ldquo;OK&rdquo; to save and ensure the newly created entry is listed as &ldquo;Status: Normal&rdquo;.</li>
</ul>
</li>
</ul>
</li>
<li>Create a file called <code>index.php</code> in your audiobook folder and save the PHP script contents (see below). Take care to edit the settings at the top of the script if necessary (e.g. to adjust the IP address).</li>
<li>Right-click on your audiobook folder, select &ldquo;Properties&rdquo;, then select the &ldquo;Permission&rdquo; tab. Check that the group (usually second entry between username and &ldquo;Everyone&rdquo;) is set to &ldquo;http&rdquo;. If it isn&rsquo;t (e.g. if it&rsquo;s set to &ldquo;users&rdquo;), double-click the group name and in the dialogue that opens, then set &ldquo;User or group&rdquo; to &ldquo;http&rdquo;.</li>
<li>Save an <code>audiobooks.png</code> cover art file in your audiobook folder. That serves as the feed cover, so something like a picture of books makes sense.</li>
<li>Navigate to <code>http://192.168.1.100:60808</code> in your browser and ensure an RSS feed is displayed. Wait for the page to load (it can take some time), do not refresh the page early!</li>
<li>Now you can add the audiobooks feed in AttennaPod. Select &ldquo;Add Podcast by RSS address&rdquo; and enter <code>http://192.168.1.100:60808</code>.</li>
<li>If you add new audiobooks, they should show up automatically when the feed is refreshed.</li>
</ol>
<p>Notes:</p>
<ul>
<li><em>The first time you load the RSS feed, expect it to be pretty slow. It takes a while to read all the metadata from the audiobook files and to save all the cover images. It&rsquo;s important to wait for that work to complete properly. Future loads should be very fast.</em></li>
<li><em>The script writes the book details to a <code>.cache.json</code> file and the cover images to <code>*.jpg</code> files. If you&rsquo;re having trouble, try deleting those files before reloading the feed.</em></li>
<li>I recommend disabling the &ldquo;Keep Updated&rdquo; option for the audiobook feed in AntennaPod. That avoids the NAS from being woken up periodically as the feed is refreshed. Just refresh the feed manually (swipe down) when you&rsquo;re queuing books.</li>
</ul>
<p>The PHP script<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#666">&lt;?</span>php
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Audiobook RSS feed generator. For more details, see
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// https://kzar.co.uk/blog/2022/08/16/setting-up-a-simple-audiobook-feed-on-a-synology-nas/
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Settings.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span><span style="color:#19177c">$feedName</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;Audiobooks&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#19177c">$feedDesc</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;Audiobooks feed description&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#19177c">$feedURL</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;http://192.168.1.100:60808&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#19177c">$coverImageFileName</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;audiobooks.png&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#19177c">$ffprobePath</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;/volume1/@appstore/ffmpeg/bin/ffprobe&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#19177c">$ffmpegPath</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;/volume1/@appstore/ffmpeg/bin/ffmpeg&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">outputRSS</span>(<span style="color:#19177c">$books</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">global</span> <span style="color:#19177c">$feedName</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">global</span> <span style="color:#19177c">$feedDesc</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">global</span> <span style="color:#19177c">$feedURL</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">global</span> <span style="color:#19177c">$coverImageFileName</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  setlocale(LC_CTYPE, <span style="color:#ba2121">&#39;en_US.UTF-8&#39;</span>);
</span></span><span style="display:flex;"><span>  header(<span style="color:#ba2121">&#39;Content-type: text/xml&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;&lt;?xml version=&#34;1.0&#34;?&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;&lt;rss version=&#34;2.0&#34; xmlns:itunes=&#34;http://www.itunes.com/dtds/podcast-1.0.dtd&#34;&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;  &lt;channel&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;title&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$feedName</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/title&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;link&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$feedURL</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/link&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;description&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$feedDesc</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/description&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;image&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;      &lt;url&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$feedURL</span> <span style="color:#666">.</span> <span style="color:#19177c">$coverImageFileName</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/url&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;      &lt;title&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$feedName</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/title&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;      &lt;link&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$feedURL</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/link&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;/image&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">foreach</span>(<span style="color:#19177c">$books</span> <span style="color:#008000;font-weight:bold">as</span> <span style="color:#19177c">$book_filename</span> <span style="color:#666">=&gt;</span> <span style="color:#19177c">$book_details</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;  &lt;item&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;title&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;title&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/title&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;itunes:title&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;title&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/itunes:title&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;description&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;description&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/description&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;author&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;author&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/author&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;pubDate&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;published_date&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/pubDate&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;link&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;url&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/link&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;guid&gt;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;url&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/guid&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;enclosure url=&#34;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;url&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&#34; &#39;</span> <span style="color:#666">.</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#ba2121">&#39;length=&#34;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;size&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&#34; &#39;</span> <span style="color:#666">.</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#ba2121">&#39;type=&#34;audio/mpeg&#34; /&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> (isset(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;cover&#39;</span>])) {
</span></span><span style="display:flex;"><span>      <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;    &lt;itunes:image href=&#34;&#39;</span> <span style="color:#666">.</span> htmlspecialchars(<span style="color:#19177c">$book_details</span>[<span style="color:#ba2121">&#39;cover&#39;</span>]) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&#34; /&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;  &lt;/item&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39; &lt;/channel&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">echo</span> <span style="color:#ba2121">&#39;&lt;/rss&gt;&#39;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">main</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">global</span> <span style="color:#19177c">$feedURL</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">global</span> <span style="color:#19177c">$ffprobePath</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">global</span> <span style="color:#19177c">$ffmpegPath</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic">// Read the cache file, if it exists.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>  <span style="color:#19177c">$cache_file</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;./.cache.json&#39;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#19177c">$cached_book_details</span> <span style="color:#666">=</span> <span style="color:#008000;font-weight:bold">array</span>();
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">if</span> (file_exists(<span style="color:#19177c">$cache_file</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">$cached_book_details</span> <span style="color:#666">=</span> json_decode(file_get_contents(<span style="color:#19177c">$cache_file</span>), <span style="color:#666">2</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic">// Loop through each book (mp3/m4b) file.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>  <span style="color:#19177c">$book_details</span> <span style="color:#666">=</span> <span style="color:#008000;font-weight:bold">array</span>();
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">foreach</span> (glob(<span style="color:#ba2121">&#39;*.{m4b,M4B,mp3,MP3}&#39;</span>, GLOB_BRACE) <span style="color:#008000;font-weight:bold">as</span> <span style="color:#19177c">$book_file</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">$size</span> <span style="color:#666">=</span> filesize(<span style="color:#19177c">$book_file</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">$file_name_without_extension</span> <span style="color:#666">=</span> pathinfo(<span style="color:#19177c">$book_file</span>, PATHINFO_FILENAME);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Reuse cached book details if available and the file size hasn&#39;t changed.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#008000;font-weight:bold">if</span> (isset(<span style="color:#19177c">$cached_book_details</span>[<span style="color:#19177c">$book_file</span>]) <span style="color:#666">&amp;&amp;</span>
</span></span><span style="display:flex;"><span>        isset(<span style="color:#19177c">$cached_book_details</span>[<span style="color:#19177c">$book_file</span>][<span style="color:#ba2121">&#39;size&#39;</span>]) <span style="color:#666">&amp;&amp;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">$cached_book_details</span>[<span style="color:#19177c">$book_file</span>][<span style="color:#ba2121">&#39;size&#39;</span>] <span style="color:#666">==</span> <span style="color:#19177c">$size</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$book_details</span>[<span style="color:#19177c">$book_file</span>] <span style="color:#666">=</span> <span style="color:#19177c">$cached_book_details</span>[<span style="color:#19177c">$book_file</span>];
</span></span><span style="display:flex;"><span>      <span style="color:#008000;font-weight:bold">continue</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Book details aren&#39;t already cached, so figure them out now.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#19177c">$details</span> <span style="color:#666">=</span> <span style="color:#008000;font-weight:bold">array</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Read the book&#39;s ID3 tags (if any).
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#19177c">$id3_tags</span> <span style="color:#666">=</span> json_decode(shell_exec(
</span></span><span style="display:flex;"><span>      escapeshellarg(<span style="color:#19177c">$ffprobePath</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39; &#39;</span> <span style="color:#666">.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ba2121">&#39;-print_format json -show_entries stream=codec_name:format &#39;</span> <span style="color:#666">.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ba2121">&#39;-select_streams a:0 -v quiet &#39;</span> <span style="color:#666">.</span>
</span></span><span style="display:flex;"><span>      escapeshellarg(<span style="color:#19177c">$book_file</span>)
</span></span><span style="display:flex;"><span>    ), <span style="color:#008000;font-weight:bold">true</span>)[<span style="color:#ba2121">&#39;format&#39;</span>][<span style="color:#ba2121">&#39;tags&#39;</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Take note of the book&#39;s title.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#008000;font-weight:bold">if</span> (isset(<span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;title&#39;</span>])) {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;title&#39;</span>] <span style="color:#666">=</span> <span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;title&#39;</span>];
</span></span><span style="display:flex;"><span>    } <span style="color:#008000;font-weight:bold">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;title&#39;</span>] <span style="color:#666">=</span> <span style="color:#19177c">$file_name_without_extension</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Take note of the book&#39;s author.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#008000;font-weight:bold">if</span> (isset(<span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;artist&#39;</span>])) {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;author&#39;</span>] <span style="color:#666">=</span> <span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;artist&#39;</span>];
</span></span><span style="display:flex;"><span>    } <span style="color:#008000;font-weight:bold">else</span> <span style="color:#008000;font-weight:bold">if</span> (isset(<span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;composer&#39;</span>])) {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;author&#39;</span>] <span style="color:#666">=</span> <span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;composer&#39;</span>];
</span></span><span style="display:flex;"><span>    } <span style="color:#008000;font-weight:bold">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;author&#39;</span>] <span style="color:#666">=</span> <span style="color:#ba2121">&#39;Unknown&#39;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Take note of the book&#39;s description.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#408080;font-style:italic">// Note: Add extra newlines so that the description is easier to read in
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#408080;font-style:italic">//       mobile podcast apps.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#008000;font-weight:bold">if</span> (isset(<span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;description&#39;</span>])) {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;description&#39;</span>] <span style="color:#666">=</span> preg_replace(<span style="color:#ba2121">&#34;/</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">+/&#34;</span>, <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n\n</span><span style="color:#ba2121">&#34;</span>,
</span></span><span style="display:flex;"><span>                                             <span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;description&#39;</span>]);
</span></span><span style="display:flex;"><span>    } <span style="color:#008000;font-weight:bold">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;description&#39;</span>] <span style="color:#666">=</span> <span style="color:#ba2121">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Append the author and narrator details to the description.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;description&#39;</span>] <span style="color:#666">.=</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n\n</span><span style="color:#ba2121">&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#39;Author: &#39;</span> <span style="color:#666">.</span> <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;author&#39;</span>] <span style="color:#666">.</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\n</span><span style="color:#ba2121">&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> (isset(<span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;narrated_by&#39;</span>])) {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;description&#39;</span>] <span style="color:#666">.=</span> <span style="color:#ba2121">&#39;Narrator: &#39;</span> <span style="color:#666">.</span> <span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;narrated_by&#39;</span>];
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Fetch the cover art (if any is available) from the file if the image is
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#408080;font-style:italic">// not yet saved.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#19177c">$cover_file</span> <span style="color:#666">=</span> <span style="color:#19177c">$file_name_without_extension</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#39;.jpg&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#666">!</span>file_exists(<span style="color:#19177c">$cover_file</span>)) {
</span></span><span style="display:flex;"><span>      shell_exec(
</span></span><span style="display:flex;"><span>          escapeshellarg(<span style="color:#19177c">$ffmpegPath</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39; -loglevel quiet -i &#39;</span> <span style="color:#666">.</span>
</span></span><span style="display:flex;"><span>          escapeshellarg(<span style="color:#19177c">$book_file</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39; -vcodec copy &#39;</span> <span style="color:#666">.</span>
</span></span><span style="display:flex;"><span>          escapeshellarg(<span style="color:#19177c">$cover_file</span>)
</span></span><span style="display:flex;"><span>      );
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> (file_exists(<span style="color:#19177c">$cover_file</span>)) {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;cover&#39;</span>] <span style="color:#666">=</span> <span style="color:#19177c">$feedURL</span> <span style="color:#666">.</span> rawurlencode(<span style="color:#19177c">$cover_file</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Take note of when the book was published.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#008000;font-weight:bold">if</span> (isset(<span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;year&#39;</span>])) {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;published_date&#39;</span>] <span style="color:#666">=</span> date(DATE_RSS, strtotime(<span style="color:#19177c">$id3_tags</span>[<span style="color:#ba2121">&#39;year&#39;</span>]));
</span></span><span style="display:flex;"><span>    } <span style="color:#008000;font-weight:bold">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;published_date&#39;</span>] <span style="color:#666">=</span> date(DATE_RSS, filemtime(<span style="color:#19177c">$book_file</span>));
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Take note of the book&#39;s other details.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;url&#39;</span>] <span style="color:#666">=</span> <span style="color:#19177c">$feedURL</span> <span style="color:#666">.</span> rawurlencode(<span style="color:#19177c">$book_file</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">$details</span>[<span style="color:#ba2121">&#39;size&#39;</span>] <span style="color:#666">=</span> <span style="color:#19177c">$size</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Save the book&#39;s details to the associative array.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#19177c">$book_details</span>[<span style="color:#19177c">$book_file</span>] <span style="color:#666">=</span> <span style="color:#19177c">$details</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic">// Write all the book details to the cache file to save work next time.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>  file_put_contents(<span style="color:#19177c">$cache_file</span>, json_encode(<span style="color:#19177c">$book_details</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic">// Finally, output the RSS!
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>  outputRSS(<span style="color:#19177c">$book_details</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Ensure the feed URL ends with a &#39;/&#39; and that the ffmpeg paths look correct.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Hopefully that should make configuration less error-prone.
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span><span style="color:#008000;font-weight:bold">if</span> (substr(<span style="color:#19177c">$feedURL</span>, <span style="color:#666">-</span><span style="color:#666">1</span>) <span style="color:#666">!=</span> <span style="color:#ba2121">&#39;/&#39;</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#19177c">$feedURL</span> <span style="color:#666">.=</span> <span style="color:#ba2121">&#39;/&#39;</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">if</span> (<span style="color:#666">!</span>file_exists(<span style="color:#19177c">$ffprobePath</span>) <span style="color:#666">||</span> <span style="color:#666">!</span>file_exists(<span style="color:#19177c">$ffmpegPath</span>)) {
</span></span><span style="display:flex;"><span>  fwrite(STDERR, <span style="color:#ba2121">&#39;Incorrect $ffprobePath or $ffmpegPath.&#39;</span> <span style="color:#666">.</span> PHP_EOL);
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">exit</span>(<span style="color:#666">1</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>main();
</span></span><span style="display:flex;"><span><span style="color:#bc7a00">?&gt;</span><span style="">
</span></span></span></code></pre></div>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>PHP script copyright 2022 Dave Vandyke, released under the <a href="https://tldrlegal.com/license/mit-license">MIT license</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2022/06/12/my-implementation-of-getting-things-done-gtd</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2022/06/12/my-implementation-of-getting-things-done-gtd" />
    <title>My implementation of Getting Things Done (GTD)</title>
    <published>2022-06-12T00:00:00+00:00</published>
    <updated>2022-06-12T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I finally got around to reading <a href="https://www.goodreads.com/book/show/1633.Getting_Things_Done">Getting Things Done by David Allen</a> a few months ago, and (to my surprise) I ended up enjoying it. It contains a lot of good ideas and got me thinking about how I organise my work and life.
If you&rsquo;ve not read it already, I recommend giving it a try and taking the time to follow along with some of his suggestions. There&rsquo;s a decent audio-book version too.</p>
<p>That said, many of the tools he uses seem antiquated to me. I don&rsquo;t see the need to write little paper notes, have a physical in-tray, or an elaborate system of ring-binders(!) to organise
my work. Since I already used Emacs and Org-mode for my notes and sometimes to-do lists, Org-mode seemed a much better option for me to implement his ideas.</p>
<p>I split up and rearranged my existing Org-mode files. I went with:</p>
<ul>
<li><code>todo.org</code> for my &ldquo;projects&rdquo;, both work and personal.</li>
<li><code>maybe-todo.org</code> for my &ldquo;someday/maybe&rdquo; ideas.</li>
<li><code>notes.org</code> for my personal notes and <code>*-notes.org</code> for work notes.</li>
</ul>
<p>I then made use of <a href="https://syncthing.net/">Syncthing</a> to synchronise those files<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<ul>
<li><code>todo.org</code> is synchronised between my personal computers and my phone.</li>
<li><code>maybe-todo.org</code> and <code>notes.org</code> are synchronised between my personal computers.</li>
<li><code>*-notes.org</code> are kept on my work computers.</li>
</ul>
<p>That minimises what data untrusted (e.g. phone and work computers) devices have while still providing access to the files as required. Using Syncthing to synchronise the files on my local network also prevents them from being shared with third parties like Dropbox.</p>
<p>I schedule dates for tasks I plan/need to do using Org-mode so that I don&rsquo;t forget to do things. I record deadlines this way too. I even have some recurring scheduled tasks, one of which is a reminder to check through all my org files every few weeks. That way, I prevent so-far unscheduled tasks from falling through the cracks.</p>
<p>I have org-agenda pointing at <code>todo.org</code> so that viewing my agenda will show upcoming tasks I need to carry out without being cluttered by items in my <code>maybe-todo.org</code> file or my notes.</p>
<figure>
  <img src="/images/blog/org-agenda.webp"
       alt="Screenshot of Org-mode Agenda view">
  <figcaption>Org-mode Agenda view.</figcaption>
</figure>
<p>I have <a href="http://www.orgzly.com/">Orgzly</a> installed on my phone, with notification alerts enabled for my scheduled agenda items. That way, I am reminded of tasks I need to complete + can check my schedule when away from my computer. I don&rsquo;t modify the to-dos from my phone, though. I found that to be fiddly and sometimes resulted in conflicting changes.</p>
<figure>
  <img src="/images/blog/orgzly-notification.webp"
       alt="Screenshot of a Orgzly notification">
  <figcaption>Orgzly notification</figcaption>
</figure>
<p>To capture my thoughts, I send myself <a href="https://signal.org/">Signal</a> messages on my phone (messages addressed to yourself show up as <a href="https://support.signal.org/hc/en-us/articles/360043272451-Note-to-Self">&ldquo;Note to Self&rdquo;</a>). I jot all my ideas down with those messages and periodically (when I&rsquo;m at my computer) go through them. I either add items to the relevant org files for each message or action them immediately for small tasks. I delete the messages as I go through them to clear out my &ldquo;in-tray&rdquo;. In the future, I plan to use Orgzly (or a different app) for capturing ideas since it seems needless to send the notes to a third-party (albeit encrypted).</p>
<p>I&rsquo;ve also stopped using my email inbox, notepad and browser tabs as ad hoc to-do lists. Periodically I clear out my tabs, emails and handwritten notes - putting anything that needs storing in the proper place. That way, &ldquo;inbox zero&rdquo; is the default.</p>
<p>I had never used <a href="https://orgmode.org/manual/Agenda-Views.html">Org-mode&rsquo;s Agenda view</a> or other Org-mode features like archiving old items. To help get up to speed, I watched Rainer König&rsquo;s excellent Udemy course <a href="https://www.udemy.com/course/getting-yourself-organized-with-org-mode/">Getting yourself organised with Org-mode</a>. I recommend checking that out if you also have gaps in your Org-mode knowledge.</p>
<p>I&rsquo;ve been using this system since I read the book in April, and I have noticed an improvement in the number of things I get done. More importantly, though, I&rsquo;ve also felt a reduction in my stress levels. Getting stuff out of my head has been a life improvement.</p>
<p>I don&rsquo;t think that using Emacs and Org-mode is for everyone. If you don&rsquo;t already use Emacs, you might be better off using <a href="https://trello.com/">Trello</a> or <a href="https://asana.com/">Asana</a> or some different software that suits you better. But whatever you choose to use, I recommend giving <a href="https://www.goodreads.com/book/show/1633.Getting_Things_Done">Getting Things Done</a> a go.</p>
<h3 id="notes">Notes</h3>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>To get Syncthing to work like that, I found the best way was to have three separate synchronised directories. Each directory can be shared with the desired devices using Syncthing and can contain the corresponding org file. I find it a little annoying having separate directories for each org file on my computer, but I soon got used to it. In particular, since with <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Desktop-Save-Mode.html">Desktop Mode</a>, I rarely have to re-open the files anyway.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2020/04/14/how-i-install-emacs-on-linux</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2020/04/14/how-i-install-emacs-on-linux" />
    <title>How I install Emacs on Linux</title>
    <published>2020-04-14T00:00:00+00:00</published>
    <updated>2020-04-14T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Every few years I need to install Emacs on another Linux system and I waste a
bunch of time remembering how to do it properly. This time I finally decided
to take some notes! I&rsquo;ve written them up as a blog post in case they&rsquo;re helpful
for other people too.</p>
<p>In the past, I&rsquo;ve needed to install very recent versions of Emacs, if that&rsquo;s
you, you can <a href="https://www.emacswiki.org/emacs/EmacsSnapshotAndDebian">install one of the Emacs snapshot packages</a>. These days
however, I find that the versions packaged in Debian/Ubuntu are recent enough
for my purposes<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. For example, at the time of writing, Debian Buster provides
26.1 and Ubuntu Eon provides 26.3. Unless you have a good reason, I recommend
just going with the version your Linux distribution provides.</p>
<p>Launching Emacs can take a while if your configuration is of a reasonable
size. Luckily though, <a href="https://www.emacswiki.org/emacs/EmacsAsDaemon#toc2">Emacs has a client/server mode</a> which allows you to
have a headless copy of Emacs start as your Emacs server at boot time.
Then anytime you open Emacs, you&rsquo;re really just opening a client which connects
to your pre-existing Emacs server. It&rsquo;s way faster.</p>
<p>Running Emacs like that provides some great flexibility too. For example, I can
SSH to my desktop computer, fire up an Emacs client and continue working with
all my existing buffers. It also means that if your system&rsquo;s GUI restarts you
don&rsquo;t lose any work, invaluable!</p>
<p>One thing to note however, the <a href="https://gitlab.gnome.org/GNOME/gtk/issues/221">GTK build of Emacs has a nasty problem</a> which
means that the Emacs server closes when the system&rsquo;s GUI restarts. Therefore I
recommend going with the Lucid build of Emacs instead. It seems a lot stabler,
though a little uglier by default - but we&rsquo;ll get to that.</p>
<p>So first, let&rsquo;s install Emacs:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt install emacs-lucid</span></span></code></pre></div>
<p>Next, we&rsquo;ll set up <a href="https://github.com/kzar/emacs.d/blob/master/emacs.service">the systemd service</a> to ensure that your Emacs server
starts when the computer boots:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir -p ~/.config/systemd/user
</span></span><span style="display:flex;"><span>wget https://raw.githubusercontent.com/kzar/emacs.d/master/emacs.service -O ~/.config/systemd/user/emacs.service
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>systemctl <span style="color:#008000">enable</span> --user emacs
</span></span><span style="display:flex;"><span>systemctl start --user emacs</span></span></code></pre></div>
<p>When you start Emacs from the application launcher, or by clicking on a file
in your file manager, you&rsquo;ll want it to connect to the existing Emacs server
instead of starting whole new copy of Emacs. We can ensure that by installing
our own <a href="https://github.com/kzar/emacs.d/blob/master/emacs.desktop">emacs.desktop</a> file:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir ~/.local/share/applications
</span></span><span style="display:flex;"><span>wget https://raw.githubusercontent.com/kzar/emacs.d/master/emacs.desktop -O ~/.local/share/applications/emacs.desktop</span></span></code></pre></div>
<p>If you need to connect to your Emacs server from the console, you can do so by
typing this:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>emacsclient -t</span></span></code></pre></div>
<p>Some terminal commands, for example <code>git config --edit</code>, will launch your
default editor. Let&rsquo;s make sure they make use of the existing Emacs server too:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#008000">echo</span> -e <span style="color:#ba2121">&#34;\nexport EDITOR=\&#34;emacsclient -c\&#34;&#34;</span> &gt;&gt; ~/.bashrc
</span></span><span style="display:flex;"><span><span style="color:#008000">export</span> <span style="color:#19177c">EDITOR</span><span style="color:#666">=</span><span style="color:#ba2121">&#34;emacsclient -c&#34;</span></span></span></code></pre></div>
<p>Next we need to configure Emacs to make the Lucid build look less ugly. I found
that by disabling the toolbars and increasing the font size it looks identical
to the GTK build. Add this to your Emacs configuration:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">first-frame-opened</span> (<span style="color:#19177c">frame</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic">;; Hide frame toolbars</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">menu-bar-mode</span> <span style="color:#666">-1</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">tool-bar-mode</span> <span style="color:#666">-1</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">scroll-bar-mode</span> <span style="color:#666">-1</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic">;; Tidy up the hook.</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">remove-hook</span> <span style="color:#19177c">&#39;after-make-frame-functions</span> <span style="color:#19177c">&#39;first-frame-opened</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">add-hook</span> <span style="color:#19177c">&#39;after-make-frame-functions</span> <span style="color:#19177c">&#39;first-frame-opened</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Include column number in the mode line.</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">setq</span> <span style="color:#19177c">column-number-mode</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Set the correct font size.</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">set-face-attribute</span> <span style="color:#19177c">&#39;default</span> <span style="color:#800">nil</span> <span style="color:#008000">:height</span> <span style="color:#666">110</span>)</span></span></code></pre></div>
<p>You&rsquo;ll need to get used to looking at the mode line to see your position in a
buffer instead of the scrollbars. I found that a little weird initially, but
after I adapted I found that it saved screen estate and was less visually
distracting.</p>
<figure>
  <img src="/images/blog/emacs-scrollbar.webp"
       alt="Screenshot of Emacs illustrating where the position information is shown">
  <figcaption>Position in buffer displayed in the Emacs mode line</figcaption>
</figure>
<p>By default, text which you highlight wont be &ldquo;yankable&rdquo;. For example, if you
highlight some text in your web browser, then hit <code>C-y</code> in Emacs the highlighted
text won&rsquo;t be pasted. That can be fixed by adding the following to your Emacs
configuration:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Make highlighted text &#34;yankable&#34;.</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">setq</span> <span style="color:#19177c">x-select-enable-primary</span> <span style="color:#800">t</span>)</span></span></code></pre></div>
<p>Emacs using some different key-bindings than the Windows etc norm. For example,
<code>C-a</code> moves to the start of the line instead of selecting all. To make the
key-bindings a bit more consistent, you can use the Emacs GTK key theme:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gsettings <span style="color:#008000">set</span> org.gnome.desktop.interface gtk-key-theme <span style="color:#ba2121">&#34;Emacs&#34;</span></span></span></code></pre></div>
<p>Or, if you&rsquo;re using XFCE:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>xfconf-query -c xsettings -p /Gtk/KeyThemeName -s Emacs</span></span></code></pre></div>
<p>I also found that by default special Unicode characters (e.g.
<span style="font: initial">😎</span>) aren&rsquo;t rendered properly. This can be
fixed with help from the Symbola font.</p>
<p>First, install the Symbola font and restart the Emacs server:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt install fonts-symbola
</span></span><span style="display:flex;"><span>systemctl restart --user emacs</span></span></code></pre></div>
<p>Then add this to your Emacs configuration:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Have special Unicode characters render with the Symbola font.</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">when</span> (<span style="color:#00f">member</span> <span style="color:#ba2121">&#34;Symbola&#34;</span> (<span style="color:#00f">font-family-list</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">set-fontset-font</span> <span style="color:#800">t</span> <span style="color:#19177c">&#39;unicode</span> <span style="color:#ba2121">&#34;Symbola&#34;</span> <span style="color:#800">nil</span> <span style="color:#19177c">&#39;prepend</span>))</span></span></code></pre></div>
<p>Now special characters in Emacs buffers should be displayed properly.</p>
<figure>
  <img src="/images/blog/emacs-unicode.webp"
       alt="Screenshot of Emacs displaying Unicode characters">
  <figcaption>Special Unicode characters being displayed in Emacs</figcaption>
</figure>
<p>If you&rsquo;re using a desktop computer, I recommend enabling <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Desktop-Save-Mode.html">Desktop-Save mode</a>.
That will maintain any file buffers that you have open between power cycles.
It is especially useful when working on larger codebases. Without
<a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Desktop-Save-Mode.html">Desktop-Save mode</a>, I often found myself considering the trade-off between
finding &amp; opening all the relevant files again tomorrow or leaving the computer
on overnight and wasting electricity.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; desktop-mode appears to have a bug when used in combination with Emacs</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; server. As a workaround, we force loading the Desktop state, even when</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; it is considered locked.</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">setq</span> <span style="color:#19177c">desktop-load-locked-desktop</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; Use desktop-mode to maintain open buffers. This means I can shut down the</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; computer more often to save power.</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">desktop-save-mode</span> <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">desktop-read</span>)</span></span></code></pre></div>
<p>To get spell checking to work, I recommend installing Hunspell and a language
pack before restarting the Emacs server:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt install hunspell hunspell-en-gb
</span></span><span style="display:flex;"><span>systemctl restart --user emacs</span></span></code></pre></div>
<p>Unless you also use British English, replace <code>hunspell-en-gb</code> with the language
pack of your choice.</p>
<p>And finally, add the corresponding Emacs lisp to your configuration (again
you&rsquo;ll need to replace <code>&quot;en_GB&quot;</code> with the appropriate language code):</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-emacs-lisp" data-lang="emacs-lisp"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Set up the Hunspell spell-checker.</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">setq</span> <span style="color:#19177c">ispell-program-name</span> <span style="color:#ba2121">&#34;hunspell&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">flyspell-issue-welcome-flag</span> <span style="color:#800">nil</span>
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">ispell-local-dictionary</span> <span style="color:#ba2121">&#34;en_GB&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">ispell-local-dictionary-alist</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">&#39;</span>((<span style="color:#ba2121">&#34;en_GB&#34;</span> <span style="color:#ba2121">&#34;[[:alpha:]]&#34;</span> <span style="color:#ba2121">&#34;[^[:alpha:]]&#34;</span> <span style="color:#ba2121">&#34;[&#39;]&#34;</span> <span style="color:#800">nil</span> (<span style="color:#ba2121">&#34;-d&#34;</span> <span style="color:#ba2121">&#34;en_GB&#34;</span>) <span style="color:#800">nil</span> <span style="color:#19177c">utf-8</span>)))</span></span></code></pre></div>
<p>Now you should be good to go, try it out with <code>M-x flyspell-mode</code>.</p>
<p>Of course, configuring Emacs once it&rsquo;s installed is a whole other topic! There&rsquo;s
no way to cover that in a blog post, but if you&rsquo;re curious to take a look
<a href="https://github.com/kzar/emacs.d">my Emacs configuration is online</a>.</p>
<br>
<h3 id="notes">Notes</h3>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>One thing that it might be worth using a snapshot build for is colour font
support. For example, Ubuntu 20.04 comes with the <code>fonts-noto-color-emoji</code>
package with great looking colour emojis. If you&rsquo;d like to try using a
snapshot build of Emacs, I recommend going for the
<a href="http://emacs.secretsauce.net/">Debian APT repository</a>, even if you&rsquo;re running Ubuntu. That includes
the <code>emacs-snapshot-lucid</code> package, unlike the Ubuntu repository. If you
are installing it on Ubuntu, you might need to install the
<code>libjpeg62-turbo</code> Debian package manually first. Once you have a snapshot
build running, you can replace <code>&quot;Symbola&quot;</code> with <code>&quot;Noto Color Emoji&quot;</code> in
your Emacs configuration.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2020/03/15/staying-sane-when-working-from-home</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2020/03/15/staying-sane-when-working-from-home" />
    <title>Staying sane when working from home</title>
    <published>2020-03-15T00:00:00+00:00</published>
    <updated>2020-03-15T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I&rsquo;ve been working remotely as a programmer for years now. Sometimes I work from
co-working spaces or cafes, but much of the time I work from home. While working
remotely is great and provides a lot of flexibility, it can be tricky to get
right. With the recent coronavirus pandemic there has been a huge push towards
remote working, so I thought I should write some practical advice for people who
are trying to work from home for the first time.</p>
<p>Remote working poses challenges on an organisational level, for example how
should people communicate, how to avoid a remote/non-remote divide, how are
decisions made and recorded? To answer those kinds of questions, you might want
to take a look at <a href="https://about.gitlab.com/company/culture/all-remote/guide/">GitLab&rsquo;s remote working guide</a> or one of the books by
Jason Fried on the subject. But many of those decisions need to be made at an
organisational level and so are likely out of your control. Keep flagging
anything that blocks you from getting your work done, and with luck those
obstacles will be systematically removed by the organisation one-by-one.</p>
<p>Remote working also poses personal challenges to the individuals working
remotely. Especially when working from home instead of a co-working space.
Staying sane, keeping work separate from personal life, focusing on work,
keeping healthy, keeping sociable, etc can all be a challenge.</p>
<p>When working on-site in an office, there&rsquo;s a lot to take for granted. You have
to get up on time, wash, get dressed, eat, physically leave the house, get some
sunlight and fresh air, get some amount of exercise travelling to the office and
talk to people at some point. You&rsquo;ll often take a lunch break and walk somewhere
to eat. You have no opportunity to do your laundry or house-hold chores when at
work, and hopefully when you return home no opportunity to continue working
either.</p>
<p>In my experience, the trick is to consider those aspects of working on-site
which you might otherwise take for granted, and figure out how to achieve
similar results working from home. Here are some concrete examples which help
me:</p>
<ul>
<li>Try to go to bed and get up at a regular time.</li>
<li>Always take a shower, get dressed and have breakfast before starting work.</li>
<li>If possible, go for a short walk or do some exercise before starting work.</li>
<li>Work regular hours, especially important is to avoid the temptation to
continue working when it&rsquo;s getting late.</li>
<li>If possible, work from somewhere different from where you relax. Don&rsquo;t work
from bed!</li>
<li>When you start work for the day, try writing down what you need to get done
with a notepad and pen. It somehow helps me focus.</li>
<li>Try listening to the radio<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> while you&rsquo;re working, or podcasts/audiobooks
while you&rsquo;re cooking or exercising.</li>
<li>If possible, switch off all work notifications when you&rsquo;re not working.</li>
<li>Try to get out of the house at least once a day for some exercise. Even if you
feel too busy. Try to time it for when the sun&rsquo;s shining!</li>
<li>If you&rsquo;re getting &ldquo;cabin-fever&rdquo; and it&rsquo;s possible, go work from a cafe or
co-working space for a few hours.</li>
</ul>
<p>I don&rsquo;t always manage all of those, in fact I often don&rsquo;t. In my experience,
working remotely effectively is an ongoing process and takes time. Be patient
with yourself. When working on-site an unproductive day doesn&rsquo;t seem so bad, you
still showed up and &ldquo;did your time&rdquo;. When working remotely, it&rsquo;s easy to panic
when you have an unproductive day - &ldquo;I&rsquo;m a fraud!&rdquo;. It happens to us all, try to
keep calm and maybe tomorrow you&rsquo;ll get more done.</p>
<h3 id="notes">Notes</h3>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Talk radio can be kind of distracting though, I recommend a station like
<a href="https://radioparadise.com/">Radio paradise</a> which is mostly music.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2019/10/15/firefox-extension-development</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2019/10/15/firefox-extension-development" />
    <title>Firefox extension development</title>
    <published>2019-10-15T00:00:00+00:00</published>
    <updated>2019-10-15T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I&rsquo;ve been working as a developer on the Adblock Plus browser extension for the
past 5 years or so. While I test my work on a bunch of different platforms, I&rsquo;ve
mostly been using Chrome during development and as my main browser. That&rsquo;s now
going to change, I am switching to Firefox.</p>
<p>I spent some time last week making the switch, I thought I&rsquo;d write up my notes
in case any other extension developers (<em>cough</em> Sebastian) are interested in
making the switch too.</p>
<p>Mozilla have written some great documentation, I recommend reading through
<a href="https://extensionworkshop.com/">extensionworkshop.com</a> before you go any further. While it seems to be
aimed at someone starting out with extension development, its still well worth
a read. There are quite a few <a href="https://extensionworkshop.com/documentation/develop/user-experience-best-practices/">cool Web Extension APIs</a> unique to Firefox
which are detailed. You will want to read up on their
<a href="https://extensionworkshop.com/documentation/develop/getting-started-with-web-ext/">web-ext command line tool</a> too. Also,
<a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Sharing_objects_with_page_scripts">the explanation of Firefox&rsquo;s &ldquo;X-ray vision&rdquo;</a> (the equivalent of
<a href="https://developer.chrome.com/extensions/content_scripts#isolated_world">Chrome&rsquo;s &ldquo;isolated worlds&rdquo;</a>) for content scripts is important to read.</p>
<p>I recommend installing the <a href="https://www.mozilla.org/firefox/developer/">Developer Edition of Firefox</a>. It&rsquo;s built from
the Beta channel, but additionally has some developer-only options enabled. It&rsquo;s
good to test against the upcoming browsers as an extension developer anyway -
better to catch problems before your users! - but specifically, the Developer
Edition is a necessity for extension development IMO for reasons I&rsquo;ll go into in
a minute.</p>
<p>When I was first getting started, I found Firefox ran kind of sluggishly to the
point of feeling almost unstable. At one point, this got so bad that the entire
Firefox window would not render at all. Initially, it looked like the browser
had crashed, but then I realised it was actually still running&hellip; That seemed
quite unusual, so on a whim, I killed my <a href="https://github.com/chjj/compton">window compositor - Compton</a>. Sure
enough, Firefox immediately reappeared. Better yet, Firefox then felt much more
responsive too. I can&rsquo;t find it now, but one comment online mentioned that
Compton can clash with applications using 3d acceleration as Firefox does.
Perhaps I had configured it incorrectly, but I&rsquo;ve just stopped using Compton for
now. Hopefully, <a href="https://simon.shimmerproject.org/2019/07/01/xfce-4-14pre2-released/">XFCE&rsquo;s improved built-in compositor</a> will be enough to avoid
screen tearing instead.</p>
<p>When loading an unpacked extension by hand, I was surprised that you have to
select its <code>manifest.json</code> file manually, instead of just selecting the
extension&rsquo;s directory. While a relatively minor annoyance it does seem pointless
so I&rsquo;ve <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1588568">filed this bug</a>.</p>
<p>Since I&rsquo;m mostly working on one extension, I want to be able to keep the
extension running all the time in order to <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">&ldquo;dogfood&rdquo;</a> my work.
In Chrome that&rsquo;s pretty easy to do, you toggle developer mode, load the unpacked
extension and then it&rsquo;ll stick around just like any other extension. With
Firefox it&rsquo;s a bit harder. While you can launch Firefox with your extension
using the <code>web-ext</code> tool, or by loading it as a temporary Add-on, the
extension and its settings won&rsquo;t persist the next time you start Firefox.</p>
<p>Having a persistent unpacked extension is possible in Firefox though, while
<a href="https://extensionworkshop.com/documentation/develop/testing-persistent-and-restart-features/">Mozilla&rsquo;s docs touch on how</a> I think this is one area the docs could
be improved. Here&rsquo;s how to do it:</p>
<ol>
<li>Browse to <code>about:config</code> and set the <code>xpinstall.signatures.required</code>
preference to <code>false</code>. (This part won&rsquo;t work with regular Firefox builds,
which is why I recommend using the Developer Edition.)</li>
<li>Figure out where your Firefox profile directory is by clicking
Help -&gt; Troubleshooting Information -&gt; Profile Directory.</li>
<li>Ensure your extension&rsquo;s manifest defines an <a href="https://extensionworkshop.com/documentation/develop/testing-persistent-and-restart-features/#what-is-an-add-on-id">extension ID</a>, specifically
it needs to be in <code>applications.gecko.id</code>.</li>
<li>Write the path of your unpacked extension directory to a text file. Name the
file your extension&rsquo;s ID (no file extension) and put it in the extensions
subdirectory of your Firefox&rsquo;s profile. In other words, type something like
this:</li>
</ol>
<pre tabindex="0"><code>echo &#34;~/my-unpacked-extension/&#34; &gt; ~/.mozilla/firefox/PROFILEDIR/extensions/EXTENSIONID&#34;
</code></pre><ol start="5">
<li>Restart Firefox.</li>
<li>Browse to <code>about:addons</code> and enable your extension.</li>
</ol>
<figure>
  <img src="/images/blog/firefox-extension-unverified.webp"
       alt="Unpacked unverified extension">
  <figcaption>Persistent unpacked extension include this "unverified" warning in Firefox</figcaption>
</figure>
<p>One thing that&rsquo;s quite different when developing extensions in Firefox vs Chrome
is how the consoles for the various extension contexts are exposed. In Chrome,
you must click the &ldquo;background page&rdquo; button from <code>chrome://extensions/</code> to open
the console for the extension&rsquo;s background page. Then right-click on the
extension&rsquo;s icon and click &ldquo;Inspect pop-up&rdquo; to debug the popup window. You can
open the console for the options or other extension pages like you would any
other webpage. Then finally to debug content scripts you open the console for a
webpage, but then use the context drop-down on the top left to switch to the
content script&rsquo;s context.</p>
<p>Firefox instead provides a <a href="https://developer.mozilla.org/en-US/docs/Tools/Browser_Toolbox">unified Toolbox window</a> for the extension. From
<code>about:addons</code> you select &ldquo;Debug Add-ons&rdquo; and from there you can click the
&ldquo;Inspect&rdquo; button and a toolbox window will open. From there you can
<a href="https://developer.mozilla.org/en-US/docs/Tools/Working_with_iframes">click on the icon</a> and switch between almost all of the extension&rsquo;s
contexts, even things like the options page. What&rsquo;s more, when switching
contexts with the Firefox developer tools you&rsquo;re switching the context for all
the tools, not just the console! So for example, the element inspector and all
the other tools change too - cool!</p>
<figure>
  <img src="/images/blog/firefox-context-switching.webp"
       alt="Switching extension contexts">
  <figcaption>Switching between extension contexts</figcaption>
</figure>
<p>Firefox&rsquo;s <a href="https://extensionworkshop.com/documentation/develop/debugging">extension Toolbox</a> is a boon, but there are a few caveats&hellip;</p>
<p>I found that the background consoles for extensions are a little noisier on
Firefox than on Chrome. Unrelated exceptions seem to show up there. For example,
with Adblock Plus and LastPass installed, I see exceptions relating to LastPass
in the background console for Adblock Plus&hellip; weird. I guess before
investigating why an exception has happened&hellip; first, check that it actually
relates to your extension! The behaviour seems undesirable, so I&rsquo;ve
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1588753">filed a bug</a>.</p>
<figure>
  <img src="/images/blog/firefox-noisy-background.webp"
       alt="Noisy background console">
  <figcaption>Noise from a different extension in the console</figcaption>
</figure>
<p>Debugging your extension&rsquo;s popup window is also a little trickier. In Chrome,
when you click to inspect the popup, the console opens and the popup window
stays open until you close it. That&rsquo;s not the case in Firefox, you need to first
select the &ldquo;Disable Popup Auto-Hide&rdquo; button from the hamburger menu of your
extension toolbox. Then you need to open your popup window, then finally select
your popup window from the context switcher in your extension toolbox.
Surprisingly that will also cause <em>all</em> extension popups to become sticky.
Before I disabled Compton I found the behaviour even more bizarre,
Firefox menus were also staying open and eventually rendering stopped entirely.</p>
<p>I found that breakpoints in my popup scripts wouldn&rsquo;t fire until I refreshed
the entire popup window. To be fair, this is also a problem I&rsquo;ve experienced
when debugging popup scripts in Chrome. I&rsquo;ve found this troublesome when
investigating problems in the past however since refreshing the popup window
from its console causes the active window to be different.</p>
<p>Unfortunately, the context switcher in Firefox&rsquo;s developer tools doesn&rsquo;t yet
support content scripts. Though if you open the developer tools for a page, you
will see your content scripts listed, their output in the console etc. One trick
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1413872#c0">I found on Bugzilla</a> is to create a breakpoint in one of your content
scripts, then once it&rsquo;s triggered hit Esc to open the console and evaluate what
you need to in the content script&rsquo;s context.</p>
<p>Anyway, I&rsquo;m really enjoying playing with Firefox again. The
<a href="https://www.smashingmagazine.com/2019/10/guide-new-experimental-css-devtools-firefox/">developer tools have some great functionality</a> that&rsquo;s missing from the
other browsers. I also can&rsquo;t wait to try some of the extra Web Extension APIs
which it supports. If it&rsquo;s been a while since you&rsquo;ve tried Firefox as a
web/extension developer it&rsquo;s well worth giving it another shot.</p>
%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2015/12/05/not-an-entrepreneur</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2015/12/05/not-an-entrepreneur" />
    <title>Not an entrepreneur</title>
    <published>2015-12-05T00:00:00+00:00</published>
    <updated>2015-12-05T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I had a couple of realisations recently that in hindsight should have been
obvious:</p>
<p><strong>I love to code, and often I even get paid to do it.</strong></p>
<p>I should pause to appreciate that and have fun with my work!</p>
<p>I shouldn&rsquo;t feel guilty when I write code&hellip;</p>
<ul>
<li>that doesn&rsquo;t make anyone money</li>
<li>that I might throw away</li>
<li>just to learn</li>
<li>purely for the <strike>fuck</strike> fun of it</li>
</ul>
<blockquote>
<p>Time enjoyed is not time wasted <a href="http://quoteinvestigator.com/2010/06/11/time-you-enjoy/">&ndash;Unknown</a></p>
</blockquote>
<p><strong>I&rsquo;m not an entrepreneur.</strong></p>
<p>&hellip;and I don&rsquo;t want to be! I can barely even <em>spell</em> the word! To repeat myself,
I shouldn&rsquo;t feel guilty when I write code that doesn&rsquo;t make anyone money. I also
don&rsquo;t have to worry about being a go-getter or doing other things that I frankly
don&rsquo;t enjoy doing.</p>
<p>I get paid enough to live comfortably doing what I enjoy to do. Why on earth did
I feel guilty for not <a href="http://paulgraham.com/articles.html">founding a startup</a>?!</p>
<blockquote>
<p>The thing that I should wish to obtain from money would be leisure with
security.     &ndash;Bertrand Russell</p>
</blockquote>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/ENfyOndcvP0"
        frameborder="0" allowfullscreen></iframe>
<p>When Sam Aaron gave his above keynote for OSCON Europe 2015, it resonated
with me. Here we were at a conference about Open Source software listening to
talks about how to make money, growth hacking and business models. Where were
the <a href="https://youtu.be/LQSWi3QJV8s?t=19m48s">passionate, pointless rants about coding style</a>, or the people that care
more about code than they do about the money it can make?</p>
<p>So this weekend I&rsquo;ll be having a go at re-writing a fairly hairy legacy Python
script in Emacs Lisp. It might work<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, it might not. I might finish, I might
not. Someone might even care, but they probably won&rsquo;t&hellip;</p>
<p><em>and I don&rsquo;t care. I&rsquo;m playing!</em></p>
<iframe width="420" height="315" src="https://www.youtube-nocookie.com/embed/HKTSaezB4p8"
        frameborder="0" allowfullscreen></iframe>
<p>&hellip;and I&rsquo;ll leave you with the words of the drummer<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>:</p>
<blockquote>
<p>At Cornell, I&rsquo;d work on preparing my courses, and I&rsquo;d go over to the library a lot and read through the Arabian Nights and ogle the girls that would go by.  But when it came time to do some research, I couldn&rsquo;t get to work.  I was a little tired; I was not interested; I couldn&rsquo;t do research! &hellip; I simply couldn&rsquo;t get started on any problem: I remember writing one or two sentences about some problem in gamma rays and then I couldn&rsquo;t go any further.  I was convinced that from the war and everything (the death of my wife) I had simply burned myself out.</p>
<p>Then I had another thought: Physics disgusts me a little bit now, but I used to enjoy doing physics. Why did I enjoy it? I used to play with it. I used to do whatever I felt like doing - it didn&rsquo;t have to do with whether it was important for the development of nuclear physics, but whether it was interesting and amusing for me to play with. When I was in high school, I&rsquo;d see water running out of a faucet growing narrower, and wonder if I could figure out what determines that curve. I found it was rather easy to do. I didn&rsquo;t have to do it; it wasn&rsquo;t important for the future of science; somebody else had already done it. That didn&rsquo;t make any difference: I&rsquo;d invent things and play with things for my own entertainment.</p>
<p>So I got this new attitude. Now that I am burned out and I&rsquo;ll never accomplish anything, I&rsquo;ve got this nice position at the university teaching classes which I rather enjoy, and just like I read the Arabian Nights for pleasure, I&rsquo;m going to play with physics, whenever I want to, without worrying about any importance whatsoever.</p>
<p>Within a week I was in the cafeteria and some guy, fooling around, throws a plate in the air. As the plate went up in the air I saw it wobble, and I noticed the red medallion of Cornell on the plate going around. It was pretty obvious to me that the medallion went around faster than the wobbling.</p>
<p>I had nothing to do, so I start to figure out the motion of the rotating plate. I discover that when the angle is very slight, the medallion rotates twice as fast as the wobble rate - two to one. It came out of a complicated equation! Then I thought, &ldquo;Is there some way I can see in a more fundamental way, by looking at the forces or the dynamics, why it&rsquo;s two to one?&rdquo;</p>
<p>I don&rsquo;t remember how I did it, but I ultimately worked out what the motion of the mass particles is, and how all the accelerations balance to make it come out two to one.</p>
<p>I still remember going to Hans Bethe and saying, &ldquo;Hey, Hans! I noticed something interesting. Here the plate goes around so, and the reason it&rsquo;s two to one is. . .&rdquo; and I showed him the accelerations.</p>
<p>He says, &ldquo;Feynman, that&rsquo;s pretty interesting, but what&rsquo;s the importance of it? Why are you doing it?&rdquo;</p>
<p>&ldquo;Hah!&rdquo; I say. &ldquo;There&rsquo;s no importance whatsoever. I&rsquo;m just doing it for the fun of it.&rdquo; His reaction didn&rsquo;t discourage me; I had made up my mind I was going to enjoy physics and do whatever I liked.</p>
<p>I went on to work out equations of wobbles. Then I thought about how electron orbits start to move in relativity. Then there&rsquo;s the Dirac Equation in electrodynamics. And then quantum electrodynamics. And before I knew it (it was a very short time) I was &ldquo;playing&rdquo; - working, really with the same old problem that I loved so much, that I had stopped working on when I went to Los Alamos: my thesis-type problems; all those old-fashioned, wonderful things.</p>
<p>It was effortless. It was easy to play with these things. It was like uncorking a bottle: Everything flowed out effortlessly. I almost tried to resist it! There was no importance to what I was doing, but ultimately there was. The diagrams and the whole business that I got the Nobel Prize for came from that piddling around with the wobbling plate.</p>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>That was a lie. I already have a proof of concept working.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Taken from <a href="http://just.thinkofit.com/feynman-on-work-and-play/">Just Think of It</a>, which was in turn taken from the biographical Surely You&rsquo;re Joking, Mr. Feynman! A great read if you have the chance.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2014/01/18/chrome-extension-automatic-updates-bitbucket-private</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2014/01/18/chrome-extension-automatic-updates-bitbucket-private" />
    <title>Automatic updates for Chrome extension hosted in private Bitbucket repository</title>
    <published>2014-01-18T00:00:00+00:00</published>
    <updated>2014-01-18T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>A while back at work I wrote a Chrome extension, and we wanted it to automatically update itself straight from the Git repository. I ran into two problems doing this however:</p>
<ol>
<li>The extension did not send the browser&rsquo;s cookie along with the request for the updates.xml, and therefore the request was denied by Bitbucket. Without the cookie, you are logged out as far as Bitbucket is concerned, and for a private repository, that means no access!</li>
<li>Bitbucket annoyingly returned the <code>X-Content-Type-Options: nosniff</code> header which <a href="http://developer.chrome.com/extensions/hosting.html">stops Chrome extensions from proceeding</a> with the update.</li>
</ol>
<p>Luckily the Chrome extension API provides support for <a href="http://developer.chrome.com/extensions/cookies.html">reading cookies</a> and <a href="http://developer.chrome.com/extensions/webRequest.html">modifying web requests + responses</a>. Which is everything we need to have our plugin trick itself into updating!</p>
<p>To do this, you&rsquo;ll need to enable the following permissions in your <code>manifest.json</code>:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>[<span style="color:#ba2121">&#34;background&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#ba2121">&#34;webRequest&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#ba2121">&#34;webRequestBlocking&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#ba2121">&#34;cookies&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#ba2121">&#34;*://*/&#34;</span>]</span></span></code></pre></div>
<p>You&rsquo;ll need to intercept relevant responses and strip out the <code>X-Content-Type-Options</code> header:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>chrome.webRequest.onHeadersReceived.addListener(<span style="color:#008000;font-weight:bold">function</span> (details) {
</span></span><span style="display:flex;"><span>  details.responseHeaders.forEach(<span style="color:#008000;font-weight:bold">function</span> (header, i) {
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> (header.name <span style="color:#666">===</span> <span style="color:#ba2121">&#34;X-Content-Type-Options&#34;</span>) {
</span></span><span style="display:flex;"><span>      details.responseHeaders.splice(i, <span style="color:#666">1</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">return</span> {
</span></span><span style="display:flex;"><span>    responseHeaders<span style="color:#666">:</span> details.responseHeaders
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}, { urls<span style="color:#666">:</span> [<span style="color:#ba2121">&#34;https://bitbucket.org/kzar/chrome-extension-update-example/raw/HEAD/*&#34;</span>] },
</span></span><span style="display:flex;"><span>[<span style="color:#ba2121">&#34;responseHeaders&#34;</span>, <span style="color:#ba2121">&#34;blocking&#34;</span>]
</span></span><span style="display:flex;"><span>);</span></span></code></pre></div>
<p>and, if you want this to work from a private repository, you&rsquo;ll need to shove the Bitbucket cookie into relevant requests:
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">var</span> bitbucket_cookie <span style="color:#666">=</span> <span style="color:#ba2121">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>chrome.cookies.getAll({ url <span style="color:#666">:</span> <span style="color:#ba2121">&#34;https://bitbucket.org&#34;</span> }, <span style="color:#008000;font-weight:bold">function</span> (cookies) {
</span></span><span style="display:flex;"><span>  cookies.forEach(<span style="color:#008000;font-weight:bold">function</span> (cookie) {
</span></span><span style="display:flex;"><span>    bitbucket_cookie <span style="color:#666">+=</span> cookie.name <span style="color:#666">+</span> <span style="color:#ba2121">&#34;=&#34;</span> <span style="color:#666">+</span> cookie.value <span style="color:#666">+</span> <span style="color:#ba2121">&#34;; &#34;</span>;
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>  bitbucket_cookie <span style="color:#666">=</span> bitbucket_cookie.slice(<span style="color:#666">0</span>, <span style="color:#666">-</span><span style="color:#666">2</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>chrome.webRequest.onBeforeSendHeaders.addListener(<span style="color:#008000;font-weight:bold">function</span> (details) {
</span></span><span style="display:flex;"><span>  details.requestHeaders.push({ name<span style="color:#666">:</span> <span style="color:#ba2121">&#39;Cookie&#39;</span>, value<span style="color:#666">:</span> bitbucket_cookie });
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">return</span> {
</span></span><span style="display:flex;"><span>    requestHeaders<span style="color:#666">:</span> details.requestHeaders
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}, { urls<span style="color:#666">:</span> [<span style="color:#ba2121">&#34;https://bitbucket.org/kzar/chrome-extension-update-example/raw/HEAD/*&#34;</span>] },
</span></span><span style="display:flex;"><span>[<span style="color:#ba2121">&#34;requestHeaders&#34;</span>, <span style="color:#ba2121">&#34;blocking&#34;</span>]
</span></span><span style="display:flex;"><span>);</span></span></code></pre></div></p>
<p>Of course, you&rsquo;ll need to have a <code>updates.xml</code> file in your repository that points to the latest packaged <code>.crx</code> for your extension in your repository as well. Also, you&rsquo;ll have to adjust the URLs.</p>
<p>It&rsquo;s worth doing though, I found being able to push a new version of your code along with updates to <code>updates.xml</code>, <code>manifest.json</code> and the plugin <code>.crx</code> file and have your users updated automatically with zero infrastructure was really handy! Much easier than nagging them to upgrade every time you fix something.</p>
<p>Now for those of you with an astute eye, the example snippets have a URL that looks plausibly like a real Bitbucket repository. Good news, <em>it is</em>! You can even try it right now for yourself, download <a href="https://bitbucket.org/kzar/chrome-extension-update-example/src/HEAD/old-chrome-extension-update-example.crx">old-chrome-extension-update-example.crx</a>, install it, click to perform Chrome extension updates and you should see it upgrade from version 0.1 to 0.2. If you want to have a look at how it works <a href="https://bitbucket.org/kzar/chrome-extension-update-example/">&ldquo;Check it out&rdquo;</a> (sorry!) and see for yourself. (Please note it&rsquo;s a public repo for obvious reasons. The example should work for private repositories as well assuming your users have access to it.)</p>
<p>Cheers, Dave.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2013/12/09/disable-emacs-read-only-warning-tramp-nfs</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2013/12/09/disable-emacs-read-only-warning-tramp-nfs" />
    <title>Disabling read-only warnings in Emacs using TRAMP</title>
    <published>2013-12-09T00:00:00+00:00</published>
    <updated>2013-12-09T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Today I had a problem with watch tasks like grunt-contrib-watch not noticing changes to files inside my Vagrant VM&rsquo;s NFS share. To avoid this I wanted to edit the files over SSH using Emacs&rsquo; <code>tramp-mode</code>, but because of NFS share UID differences it proved difficult. NFS is set by Vagrant to write as the correct user, but because the host machine&rsquo;s UID doesn&rsquo;t match the VM&rsquo;s vagrant user, the files appear read-only from the VM&rsquo;s perspective. (With a Mac host at least the files are owned by <code>501:dialout</code>, instead of <code>vagrant:vagrant</code>.) This confuses TRAMP mode and ends up with an RSI inducing amount of &ldquo;File X is write-protected; try to save anyway? (yes or no)&rdquo; messages. It took far too long to figure out a way around it but, I got there in the end with help from the good folks on #vagrant and #emacs. Here&rsquo;s a snippet to brutally disable the warnings:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#19177c">add-hook</span> <span style="color:#19177c">&#39;find-file-hook</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#008000">lambda</span> ()
</span></span><span style="display:flex;"><span>            (<span style="color:#008000">when</span> (<span style="color:#19177c">file-remote-p</span> (<span style="color:#19177c">buffer-file-name</span>))
</span></span><span style="display:flex;"><span>              (<span style="color:#00f">set</span> (<span style="color:#19177c">make-local-variable</span> <span style="color:#19177c">&#39;inhibit-read-only</span>) <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>              (<span style="color:#19177c">fset</span> (<span style="color:#19177c">make-local-variable</span> <span style="color:#19177c">&#39;file-writable-p</span>) (<span style="color:#008000">lambda</span> (<span style="color:#19177c">filename</span>) <span style="color:#800">t</span>))
</span></span><span style="display:flex;"><span>              (<span style="color:#00f">set</span> (<span style="color:#19177c">make-local-variable</span> <span style="color:#19177c">&#39;buffer-read-only</span>) <span style="color:#800">nil</span>))))</span></span></code></pre></div>
<p>Here&rsquo;s hoping Vagrant will support something <a href="https://github.com/mitchellh/vagrant/pull/2587">like this :nfs_guest feature soon</a>, it <a href="https://gist.github.com/kzar/7880173">certainly sounds like the developers are on the case</a>. But in the meantime at least I can get on with some coding!</p>
<p>Cheers, Dave.</p>
<p>Edit:</p>
<p>I&rsquo;ve also written a shell script to help workaround this problem for other developers on the team (who don&rsquo;t use Emacs). The script watches for changes locally and touches the corresponding file over SSH.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#bc7a00">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#bc7a00"></span><span style="color:#008000;font-weight:bold">while</span> <span style="color:#008000">true</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">do</span>
</span></span><span style="display:flex;"><span>    find . -mtime -1s -type f -exec vagrant ssh -c <span style="color:#ba2121">&#34;touch ~/remote/base/path/{}&#34;</span> <span style="color:#b62;font-weight:bold">\;</span>
</span></span><span style="display:flex;"><span>    sleep <span style="color:#666">1</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">done</span></span></span></code></pre></div>
<p>(It assumes the Vagrantfile is in the project root, and that ~/remote/base/path is the path to your project, in the NFS mount, on the remote machine.)</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2013/10/01/wordpress-rewrite-rules</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2013/10/01/wordpress-rewrite-rules" />
    <title>WordPress rewrite rules</title>
    <published>2013-10-01T00:00:00+00:00</published>
    <updated>2013-10-01T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I found the WordPress documentation about custom rewrite rules pretty confusing and most of the blog posts / Stackoverflow posts I could find weren&rsquo;t much better. Silly thing is it&rsquo;s actually pretty simple to set up your own rewrite rules when you know how. Here&rsquo;s what I figured out:</p>
<ul>
<li>You want to create your rewrite rules on the WordPress <code>init</code> action hook AND the plugin <code>activation</code> hook.</li>
<li>You should also flush your rewrite rules after creating them in the <code>activation</code> hook.</li>
<li>You also need to create custom &ldquo;rewrite_tags&rdquo; to access route parameters in the <code>init</code> hook.</li>
<li>The rewrite rule seemingly has to point to <code>index.php</code>, so you have to use GET parameters instead of using pretty links in the rule.</li>
<li>Pretty permalinks must already be enabled!</li>
<li>Flushing the rules on the <code>deactivation</code> hook doesn&rsquo;t seem to do much good!</li>
</ul>
<p>So let&rsquo;s look at an example, we want to have a page called <code>items</code> that&rsquo;s controlled by our code. We also want to make it so that a user can view any item by browsing to a URL like <code>/items/view/14</code> and the code needs to be able to access the item ID.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#666">&lt;?</span>php
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">/*
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">Plugin Name: Example
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">Version: 0.1.0
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">Author URI: http://kzar.co.uk/blog/2013/10/01/wordpress-rewrite-rules/
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">*/</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">class</span> <span style="color:#00f;font-weight:bold">Example</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">private</span> <span style="color:#008000;font-weight:bold">static</span> <span style="color:#19177c">$instance</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">__construct</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Set up our hooks
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    add_action(<span style="color:#ba2121">&#39;init&#39;</span>, <span style="color:#008000;font-weight:bold">array</span>(<span style="color:#19177c">$this</span>, <span style="color:#ba2121">&#39;init&#39;</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    register_activation_hook(<span style="color:#800">__FILE__</span>, <span style="color:#008000;font-weight:bold">array</span>(<span style="color:#19177c">$this</span>, <span style="color:#ba2121">&#39;activate&#39;</span>));
</span></span><span style="display:flex;"><span>    register_deactivation_hook(<span style="color:#800">__FILE__</span>, <span style="color:#008000;font-weight:bold">array</span>(<span style="color:#19177c">$this</span>, <span style="color:#ba2121">&#39;deactivate&#39;</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    add_shortcode(<span style="color:#ba2121">&#39;example-plugin&#39;</span>, <span style="color:#008000;font-weight:bold">array</span>(<span style="color:#19177c">$this</span>, <span style="color:#ba2121">&#39;plugin_shortcode&#39;</span>));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">public</span> <span style="color:#008000;font-weight:bold">static</span> <span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">getInstance</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#666">!</span>self<span style="color:#666">::</span><span style="color:#19177c">$instance</span>) {
</span></span><span style="display:flex;"><span>      self<span style="color:#666">::</span><span style="color:#19177c">$instance</span> <span style="color:#666">=</span> <span style="color:#008000;font-weight:bold">new</span> Example();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">return</span> self<span style="color:#666">::</span><span style="color:#19177c">$instance</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">rewrite_rules</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Add our rewrite rule and rewrite tag
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    add_rewrite_rule(<span style="color:#ba2121">&#39;^items/view/([0-9]+)$&#39;</span>, <span style="color:#ba2121">&#39;index.php?pagename=items&amp;itemid=$matches[1]&#39;</span>, <span style="color:#ba2121">&#39;top&#39;</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">init</span>() {
</span></span><span style="display:flex;"><span>    add_rewrite_tag(<span style="color:#ba2121">&#39;%itemid%&#39;</span>, <span style="color:#ba2121">&#39;([0-9]+)&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">$this</span><span style="color:#666">-&gt;</span><span style="color:#7d9029">rewrite_rules</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">plugin_shortcode</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">return</span> <span style="color:#ba2121">&#39;&lt;b&gt;View ID is &#39;</span> <span style="color:#666">.</span> get_query_var(<span style="color:#ba2121">&#39;itemid&#39;</span>) <span style="color:#666">.</span> <span style="color:#ba2121">&#39;&lt;/b&gt;&#39;</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">activate</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Make sure pretty permalinks are enabled
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#008000;font-weight:bold">global</span> <span style="color:#19177c">$wp_rewrite</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#666">!</span><span style="color:#19177c">$wp_rewrite</span><span style="color:#666">-&gt;</span><span style="color:#7d9029">permalink_structure</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#008000;font-weight:bold">echo</span>(<span style="color:#ba2121">&#34;Plugin requires pretty permalinks to be enabled!&#34;</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#008000;font-weight:bold">exit</span>();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Create our items page (With shortcode already in content)
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#666">!</span>get_page_by_path(<span style="color:#ba2121">&#39;items&#39;</span>)) {
</span></span><span style="display:flex;"><span>      wp_insert_post(<span style="color:#008000;font-weight:bold">array</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#ba2121">&#39;post_title&#39;</span> <span style="color:#666">=&gt;</span> <span style="color:#ba2121">&#39;Items&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ba2121">&#39;post_content&#39;</span> <span style="color:#666">=&gt;</span> <span style="color:#ba2121">&#39;[example-plugin]&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ba2121">&#39;post_status&#39;</span> <span style="color:#666">=&gt;</span> <span style="color:#ba2121">&#39;publish&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ba2121">&#39;comment_status&#39;</span> <span style="color:#666">=&gt;</span> <span style="color:#ba2121">&#39;closed&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ba2121">&#39;ping_status&#39;</span> <span style="color:#666">=&gt;</span> <span style="color:#ba2121">&#39;closed&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ba2121">&#39;post_type&#39;</span> <span style="color:#666">=&gt;</span> <span style="color:#ba2121">&#39;page&#39;</span>
</span></span><span style="display:flex;"><span>      ));
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Create our rewrite rule
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#19177c">$this</span><span style="color:#666">-&gt;</span><span style="color:#7d9029">rewrite_rules</span>();
</span></span><span style="display:flex;"><span>    flush_rewrite_rules();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">function</span> <span style="color:#00f">deactivate</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Remove the page
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#19177c">$page</span> <span style="color:#666">=</span> get_page_by_path(<span style="color:#ba2121">&#39;items&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#19177c">$page</span>) {
</span></span><span style="display:flex;"><span>      wp_delete_post(<span style="color:#19177c">$page</span><span style="color:#666">-&gt;</span><span style="color:#7d9029">ID</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic">// Remove our rewrite rule
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    <span style="color:#408080;font-style:italic">// FIXME does not work as the init hook has already been called
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>    flush_rewrite_rules();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#19177c">$example</span> <span style="color:#666">=</span> Example<span style="color:#666">::</span><span style="color:#7d9029">getInstance</span>();</span></span></code></pre></div>
<p>Put that in your plugins directory - call it <code>example.php</code> - and then activate the Example plugin from wp-admin. You should see in the frontend that a page <code>/items</code> has been created, and if you browse to <code>/items/view/1324</code> it will display 1324 back to you. Deactivate the plugin and the page will be deleted and things will mostly be cleaned up.</p>
<p>Now, this example&rsquo;s not perfect; deactivation of the plugin does not flush away our rewrite rule! Why?! Well, as the init function is called before the plugin is deactivated, the rule is already in memory when the rules are flushed. I&rsquo;m not sure the best way to overcome that, although I don&rsquo;t think it&rsquo;s the most significant issue as the next time the rules are flushed by WordPress or a plugin our old rule will be removed. <em>(Hint you can browse to the permalinks page in wp-admin to flush your rewrite rules.)</em></p>
<p>Why do we have to create our rewrite rules from both the <code>init</code> hook and <code>activation</code> hook? Well, it appears that the <code>init</code> hook is not called until after the plugin is activated, but we can only flush on activation. So to have our rules work, we must create them at activation. But if <em>something else</em> flushes the rules, and we haven&rsquo;t created them beforehand they&rsquo;ll be removed, so we need to create them each time at activation just in case! So the only way to cover all bases appears to be to create the rewrite rules on both init and activation!</p>
<p>OK, but what&rsquo;s with the rewrite tags? Honestly, I have no idea, but if you don&rsquo;t jump through that hoop <code>get_query_var</code> doesn&rsquo;t return them, and FYI <code>$_REQUEST</code> doesn&rsquo;t contain them either.</p>
<p>Finally I&rsquo;d like to draw attention to the second parameter of our add_rewrite_rule call: <code>'index.php?pagename=items&amp;itemid=$matches[1]'</code>. Originally I tried something like <code>items/?itemid=$matches[1]</code> but that does not work, you have to make sure it&rsquo;s in terms of index.php. A bit confusing to start with but not a problem, you can do everything you need to that way using GET parameters like <code>pagename</code>.</p>
<p>Hope that helps! Dave.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2012/01/06/telit-gm862-gps-hex-bug</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2012/01/06/telit-gm862-gps-hex-bug" />
    <title>Telit GM862-GPS hex() bug</title>
    <published>2012-01-06T00:00:00+00:00</published>
    <updated>2012-01-06T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>My SHA1 code was returning a different hex digest when I ran it on my Telit GM862 GPS. I eventually tracked the problem down to the <code>hex()</code> function. I found that <code>hex(3181490320L)</code> did not return the right result on my device!</p>
<p>Run this test script and post your results below.</p>
<p><div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">import</span> <span style="color:#00f;font-weight:bold">sys</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">SER</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">MDM</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Set up printing to serial</span>
</span></span><span style="display:flex;"><span>SER<span style="color:#666">.</span>set_speed(<span style="color:#ba2121">&#39;115200&#39;</span>, <span style="color:#ba2121">&#39;8N1&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">class</span> <span style="color:#00f;font-weight:bold">SerWriter</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">write</span>(<span style="color:#008000">self</span>, s):
</span></span><span style="display:flex;"><span>    SER<span style="color:#666">.</span>send(s <span style="color:#666">+</span> <span style="color:#ba2121">&#34;</span><span style="color:#b62;font-weight:bold">\r</span><span style="color:#ba2121">&#34;</span>)
</span></span><span style="display:flex;"><span>sys<span style="color:#666">.</span>stdout <span style="color:#666">=</span> sys<span style="color:#666">.</span>stderr <span style="color:#666">=</span> SerWriter()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Simplistic AT command function</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">at_command</span>(command):
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic"># Clear the command interface buffer</span>
</span></span><span style="display:flex;"><span>  MDM<span style="color:#666">.</span>read()
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic"># Send the command</span>
</span></span><span style="display:flex;"><span>  MDM<span style="color:#666">.</span>send(command <span style="color:#666">+</span> <span style="color:#ba2121">&#39;</span><span style="color:#b62;font-weight:bold">\r</span><span style="color:#ba2121">&#39;</span>, <span style="color:#666">0</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#408080;font-style:italic"># Create a buffer</span>
</span></span><span style="display:flex;"><span>  buffer <span style="color:#666">=</span> <span style="color:#ba2121">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">while</span> <span style="color:#666">1</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic"># Listen to serial port for click</span>
</span></span><span style="display:flex;"><span>    incoming <span style="color:#666">=</span> MDM<span style="color:#666">.</span>receive(<span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#408080;font-style:italic"># If we got some data handle it</span>
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> incoming:
</span></span><span style="display:flex;"><span>      buffer <span style="color:#666">=</span> buffer <span style="color:#666">+</span> incoming
</span></span><span style="display:flex;"><span>      <span style="color:#008000;font-weight:bold">if</span> buffer<span style="color:#666">.</span>find(<span style="color:#ba2121">&#34;OK&#34;</span>) <span style="color:#666">&gt;</span> <span style="color:#666">-</span><span style="color:#666">1</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#008000;font-weight:bold">return</span> buffer
</span></span><span style="display:flex;"><span>      <span style="color:#008000;font-weight:bold">elif</span> buffer<span style="color:#666">.</span>find(<span style="color:#ba2121">&#34;ERROR&#34;</span>) <span style="color:#666">&gt;</span> <span style="color:#666">-</span><span style="color:#666">1</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#008000;font-weight:bold">return</span> <span style="color:#ba2121">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Run the tests</span>
</span></span><span style="display:flex;"><span><span style="color:#008000">print</span> at_command(<span style="color:#ba2121">&#34;AT+CGMM&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#008000">print</span> at_command(<span style="color:#ba2121">&#34;AT+CGMR&#34;</span>)
</span></span><span style="display:flex;"><span>i <span style="color:#666">=</span> <span style="color:#666">3181490320</span>L
</span></span><span style="display:flex;"><span><span style="color:#008000">print</span> i
</span></span><span style="display:flex;"><span><span style="color:#008000">print</span> long(<span style="color:#008000">hex</span>(i), <span style="color:#666">16</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Restore standard out</span>
</span></span><span style="display:flex;"><span>sys<span style="color:#666">.</span>stdout <span style="color:#666">=</span> sys<span style="color:#666">.</span>stderr <span style="color:#666">=</span> sys<span style="color:#666">.</span>__stdout__</span></span></code></pre></div>
My output was:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>OK
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>GM862-GPS
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>OK
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>07.03.402
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>OK
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#666">3181490320</span>
</span></span><span style="display:flex;"><span><span style="color:#666">2376183952</span></span></span></code></pre></div>
<p><strong>Cheers, Dave.</strong></p>
<p>Edit: In case anyone&rsquo;s interested here&rsquo;s my <code>hex()</code> replacement. It&rsquo;s probably much slower, but it works and can pad the hex string to x bytes.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">write_hex</span>(x, <span style="color:#008000">bytes</span><span style="color:#666">=</span><span style="color:#666">1</span>):
</span></span><span style="display:flex;"><span>  h <span style="color:#666">=</span> [<span style="color:#ba2121">&#39;0&#39;</span>] <span style="color:#666">*</span> <span style="color:#008000">bytes</span> <span style="color:#666">*</span> <span style="color:#666">2</span>
</span></span><span style="display:flex;"><span>  i <span style="color:#666">=</span> <span style="color:#666">0</span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">while</span> x <span style="color:#666">&gt;</span> <span style="color:#666">0</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">if</span> i <span style="color:#666">&lt;</span> <span style="color:#008000">bytes</span> <span style="color:#666">*</span> <span style="color:#666">2</span>:
</span></span><span style="display:flex;"><span>      h[i] <span style="color:#666">=</span> <span style="color:#ba2121">&#39;0123456789abcdef&#39;</span>[x <span style="color:#666">&amp;</span> <span style="color:#666">0xf</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">else</span>:
</span></span><span style="display:flex;"><span>      h<span style="color:#666">.</span>append(<span style="color:#ba2121">&#39;0123456789abcdef&#39;</span>[x <span style="color:#666">&amp;</span> <span style="color:#666">0xf</span>])
</span></span><span style="display:flex;"><span>    x <span style="color:#666">=</span> x <span style="color:#666">&gt;&gt;</span> <span style="color:#666">4</span>
</span></span><span style="display:flex;"><span>    i <span style="color:#666">=</span> i <span style="color:#666">+</span> <span style="color:#666">1</span>
</span></span><span style="display:flex;"><span>  h<span style="color:#666">.</span>reverse()
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">return</span> <span style="color:#ba2121">&#39;&#39;</span><span style="color:#666">.</span>join(h)</span></span></code></pre></div>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2011/08/22/bit-ratchet</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2011/08/22/bit-ratchet" />
    <title>bit-ratchet, easier binary parsing in PHP</title>
    <published>2011-08-22T00:00:00+00:00</published>
    <updated>2011-08-22T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Recently I needed to write some code to parse a binary protocol in PHP. Given an ASCII hex string representation of the message, I needed to pull off bits and bytes, using them in lots of different ways.</p>
<p>The problem was that I had to read a byte at a time from the hex string, then manually shift bits each time to get the desired values. That got confusing fast! So to make things easier I wrote <a href="https://github.com/kzar/bit-ratchet">bit-ratchet, a small class that lets you read bits and bytes from a hex string</a> very simply.</p>
<p>The idea is you create a bit-ratchet object from your hex string. You can then ask that object to read numbers from the data, specifying their length in bits. The bit-ratchet object keeps track of the current position in the data and all the shifting for you.</p>
<p>The library takes care of signed numbers but otherwise leaves processing like scaling numbers to the user. I wanted to avoid creating an overly complicated library like <a href="http://bindata.rubyforge.org/">BinData</a>.</p>
<p>It scratched my itch but let me know if you&rsquo;ve got any ideas about how to expand on the abstraction.</p>
<p><strong>Cheers, Dave</strong></p>
<p>Edit: I&rsquo;ve since written a <a href="https://github.com/kzar/bitratchet-js">much more powerful JavaScript version</a> of this library.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2011/05/14/embedded-development-setup-with-macbook</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2011/05/14/embedded-development-setup-with-macbook" />
    <title>Embedded development setup with MacBook</title>
    <published>2011-05-14T00:00:00+00:00</published>
    <updated>2011-05-14T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I&rsquo;ve been put onto an exciting project recently, doing some embedded Python development for a <a href="http://bit.ly/jjfgBu">Telit GM862-GPS unit</a>. Being new to this type of thing it took me a while to get a development environment set up that I was happy with. To share what I&rsquo;ve learned, here&rsquo;s my setup:</p>
<p><em>(To be clear I&rsquo;m using the <a href="http://www.roundsolutions.com/shop/products/en/GSM-Modem/Starter-Kits/GM862-GPS-Starter-Kit.html">Roundsolutions development starter kit for the Telit GM862-GPS</a> with my MacBook pro.)</em></p>
<p><img src="/images/blog/roundsolutiondevboard.webp" alt="Roundsolutions dev board"></p>
<p>First of all, I had to install the usb-serial driver (<a href="http://www.prolific.com.tw/eng/downloads.asp?id=31">PL2303_1.2.1r2.dmg</a>) to get the serial port working. Once that was installed the serial device /dev/tty.usbserial appeared for use.</p>
<p>Next, I <a href="http://www.tigoe.net/pcomp/resources/archives/avr/000749.shtml">followed this guide</a> and connected to the device using the GNU Screen command that&rsquo;s included with Snow Leopard. This command worked nicely: <code>screen -S telit -T vt100 /dev/tty.usbserial 115200,crtcts,-parity,-cstopb</code></p>
<p>Typing AT returned OK and I knew I was in business!</p>
<p>I soon found that deploying code over serial manually was pretty tedious since it&rsquo;s easy to mess up the AT commands. So I wrote <a href="https://gist.github.com/968694">this script to automate the process</a>, making deployment as easy as typing <code>telit-send-python.py telit example.py</code>.</p>
<p>The next issue I hit was that my code was sometimes being crapped up during transfer. I eventually figured out I was using the wrong line terminators for my files, Unix style uses <code>\n</code> whereas the Telit module demands <code>\n\r</code> (DOS style). To convert my files in Emacs I used the buffer-file-coding-system command, so  <code>M-x set buffer-file-coding-system undecided dos</code>.</p>
<p>Next, I realised backspace wasn&rsquo;t being transmitted properly across Screen. There&rsquo;s an option in the Advanced section of Terminal preferences &ldquo;Delete sends Ctrl-H&rdquo; which resolved it for me. Unfortunately, I found that messed up my other SSH sessions. A better solution is to have this fixed in GNU Screen, add these lines to your ~/.screenrc:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bindkey -d ^? stuff ^H
</span></span><span style="display:flex;"><span>bindkey -d ^@ stuff ^H</span></span></code></pre></div>
<p>Another annoyance with Screen was that I wasn&rsquo;t able to scroll up like normal in my Terminal windows. Luckily fixing that was pretty easy, I added this to my ~/.screenrc : <code>termcapinfo xterm* ti@:te@</code></p>
<p>Messing with SMS at commands I realised that the body of an SMS needs to be terminated with 0x1a, it&rsquo;s a bit tricky to type, but there&rsquo;s a solution, <a href="http://www.gnu.org/software/screen/manual/html_node/Digraph.html">Screen&rsquo;s &ldquo;digraph&rdquo; feature</a>. To send 0x1a here&rsquo;s what I did: <code>C-a C-v 0x1a</code>.</p>
<p>So the next thing I wanted to was to get the Telit version of Python running locally, allowing me to test things interactively at a &lsquo;REPL&rsquo;. The units run a modified version of Python 1.5.2 and unfortunately I had no luck compiling or installing a version that old under Snow Leopard. My solution, in the end, was to install the <a href="http://www.google.co.uk/#q=TelitPy1.5.2%2B">Windows version TelitPy1.5.2+_V4.1.exe</a> using Wine. I then set up a little shell-script shortcut to launch it easily. (It installs to ~/.wine/drive_c/Program\ Files/Python/python.exe )</p>
<p>Now I&rsquo;ve got a pretty sweet set up, but having the PCB dangling off the side of my MacBook in Starbucks isn&rsquo;t an option. So I plugged it into my Linux server at home, so then I could SSH in and deploy code / interact with the Terminal. (Using Emacs&rsquo; tramp feature I can edit the code directly over SSH too.)</p>
<p>The only thing worth noting is that the naming convention for serial devices seems different in Linux. Also, I found that sometimes the LANG environment variable ended up corrupting the binary transfer. Here&rsquo;s the Screen command I ended up using: <code>unset LANG &amp;&amp; screen -S telit -T vt100 /dev/ttyUSB0 115200,crtcts,-parity,-cstopb</code>.</p>
<p><em><a href="http://unixhelp.ed.ac.uk/CGI/man-cgi?stty">Read the Stty man page</a> for details about those serial options at the end.</em></p>
<p>I also sometimes needed to check what Screen&rsquo;s sending out, helpful for debugging problems with transmitting binary data. It&rsquo;s not hard though, start a Screen session and run this command inside it: <code>stty raw; cat &gt; binary-file-name</code>.</p>
<p><em>(Make sure to exit using the <code>C-a :quit screen</code> command since most special keys will be sent directly through into your file.)</em></p>
<p>Finally, while I&rsquo;m waiting for <a href="http://www.roundsolutions.com/shop/products/en/Accessories/Python-Debugger-for-Telit-Modules-GM862-GPS-or-GE863-GPS.html">the Python debugging board</a> to arrive, I needed to have some way of getting output through to the serial port. Otherwise, it&rsquo;s hard to know if your code even ran at all. It turns out it&rsquo;s not too hard, here&rsquo;s an example:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">import</span> <span style="color:#00f;font-weight:bold">SER</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">MOD</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">MDM</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">GPS</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">sys</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Set the baud rate</span>
</span></span><span style="display:flex;"><span>SER<span style="color:#666">.</span>set_speed(<span style="color:#ba2121">&#39;115200&#39;</span>, <span style="color:#ba2121">&#39;8N1&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Send stout and stderr over serial</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># http://forum.sparkfun.com/viewtopic.php?t=6289</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">class</span> <span style="color:#00f;font-weight:bold">SerWriter</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">write</span>(<span style="color:#008000">self</span>, s):
</span></span><span style="display:flex;"><span>    SER<span style="color:#666">.</span>send(s <span style="color:#666">+</span> <span style="color:#ba2121">&#39;</span><span style="color:#b62;font-weight:bold">\r</span><span style="color:#ba2121">&#39;</span>)
</span></span><span style="display:flex;"><span>sys<span style="color:#666">.</span>stdout <span style="color:#666">=</span> sys<span style="color:#666">.</span>stderr <span style="color:#666">=</span> SerWriter()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># You get the idea...</span>
</span></span><span style="display:flex;"><span><span style="color:#008000">print</span> <span style="color:#ba2121">&#34;Lovely stuff.&#34;</span></span></span></code></pre></div>
<p>Thanks for reading, I&rsquo;d be interested to hear any tips you have in the comments. Also if anyone knows a better support forum/community than the <a href="http://www.roundsolutions.com/forum/board.php?boardid=19">Roundsolutions one</a> let me know.</p>
<p><strong>Cheers, Dave.</strong></p>
<p>Edit: I&rsquo;ve just discovered <a href="http://www.sparkfun.com/products/280">Sparkfun&rsquo;s evaluation board</a>. The board looks a lot nicer, it&rsquo;s cheaper, and Sparkfun list prices and stock. I&rsquo;ve had a look through <a href="http://forum.sparkfun.com/viewforum.php?f=17">their forum</a> too, and it seems much friendlier. I&rsquo;d go with the Sparkfun board over the Roundsolutions one next time.</p>
<p>Edit 2: I&rsquo;ve had all sorts of problems uploading Python scripts larger than about 7kb to the Telit module. In the end, I updated my script to send files in chunks with enough delay for the module to catch up. I also had problems deleting large files until I removed the quotes around a file name. For more details <a href="http://bit.ly/l6HI4N">check out this forum post</a>.</p>
<p>Edit 3: Someone has just shown me <a href="http://wiki.groundlab.cc/doku.php?id=telit_python">this great guide</a> about Telit Python development, well worth reading.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2011/05/09/stop-rails-trying-to-parse-the-post--put-request-body</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2011/05/09/stop-rails-trying-to-parse-the-post--put-request-body" />
    <title>Stop Rails trying to parse the POST / PUT request body</title>
    <published>2011-05-09T00:00:00+00:00</published>
    <updated>2011-05-09T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Rails 3 has a nice feature where it will parse the body of PUT and POST requests depending on the Content-type given. So for example, if I POST XML to Rails, it will all be decoded for me and put into the params hash.</p>
<p><strong>Well, it&rsquo;s a nice feature until you try to switch it off, it&rsquo;s a <em>nightmare</em> to disable! I managed it in the end, here&rsquo;s what I did&hellip;</strong></p>
<p>I created a piece of &lsquo;rack middleware&rsquo; that overwrites the Content-Type for given paths:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># lib/no_parse.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">class</span> <span style="color:#00f;font-weight:bold">NoParse</span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">initialize</span>(app, options<span style="color:#666">=</span>{})
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">@app</span> <span style="color:#666">=</span> app
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">@urls</span> <span style="color:#666">=</span> options<span style="color:#666">[</span><span style="color:#19177c">:urls</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">call</span>(env)
</span></span><span style="display:flex;"><span>    env<span style="color:#666">[</span><span style="color:#ba2121">&#39;CONTENT_TYPE&#39;</span><span style="color:#666">]</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;text/plain&#39;</span> <span style="color:#008000;font-weight:bold">if</span> <span style="color:#19177c">@urls</span><span style="color:#666">.</span>include? env<span style="color:#666">[</span><span style="color:#ba2121">&#39;PATH_INFO&#39;</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">@app</span><span style="color:#666">.</span>call(env)
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">end</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">end</span></span></span></code></pre></div>
<p>Included it:
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">#config/application.rb</span>
</span></span><span style="display:flex;"><span>require_relative <span style="color:#ba2121">&#39;../lib/no_parse&#39;</span></span></span></code></pre></div></p>
<p>Added it into the &lsquo;stack&rsquo;:
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">#config/initializers/no_parse.rb</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># (Change the :urls to specify which paths use NoParse.)</span>
</span></span><span style="display:flex;"><span><span style="color:#800">Rails</span><span style="color:#666">.</span>configuration<span style="color:#666">.</span>middleware<span style="color:#666">.</span>insert_before(<span style="color:#ba2121">&#39;ActionDispatch::ParamsParser&#39;</span>,
</span></span><span style="display:flex;"><span>                                             <span style="color:#ba2121">&#39;NoParse&#39;</span>, <span style="color:#19177c">:urls</span> <span style="color:#666">=&gt;</span> <span style="color:#666">[</span><span style="color:#ba2121">&#39;/example-path&#39;</span><span style="color:#666">]</span>)</span></span></code></pre></div></p>
<p>Make sure you restart rails, and you should be good to go. If it&rsquo;s not working, make sure the <code>rake middleware</code> command is listing the <code>NoParse</code> class above <code>ActionDispatch::ParamsParser</code>.</p>
<p><strong>Cheers, Dave.</strong></p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2010/12/11/blocking-spotify-p2p-connections</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2010/12/11/blocking-spotify-p2p-connections" />
    <title>Blocking Spotify&#39;s P2P Connections</title>
    <published>2010-12-11T00:00:00+00:00</published>
    <updated>2010-12-11T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>It turns out Spotify uses P2P technology so that it can reduce the load on its servers. Pretty smart, but the problem is that uses quite a lot of the user&rsquo;s bandwidth, and sometimes that&rsquo;s not OK!</p>
<p>The client always falls back on their central servers so that we could block P2P. The problem is that we&rsquo;re given no manual option to disable, (or even limit) P2P sharing. Worse still, the client spawns connections to other users within a massive range of ports making them tricky to block.</p>
<p>I just spent the last few hours looking into this, since it destroyed my bandwidth quota. I&rsquo;ve got a few ideas, and some of them have worked.</p>
<h2 id="desktop-firewall">Desktop Firewall</h2>
<p>I know, I know, I hate these Desktop firewalls as well. I never thought I&rsquo;d see the day when I wanted to use one. On the other hand, it does make dealing with programs like Spotify much easier. On my MacBook, I was able to install <a href="http://www.obdev.at/products/littlesnitch/index.html">The Little Snitch</a> and set it up to block the Spotify process from connecting to anything except .spotify.com.</p>
<p><img src="/images/blog/little-snitch-spotify.webp" alt="Little snitch settings to block Spotify&rsquo;s P2P connections"></p>
<p><em><strong>It turned out to work pretty well, and it was the easiest/quickest solution. It&rsquo;s not helpful if you&rsquo;re using more than one computer though and I still don&rsquo;t like the idea of relying on programs like these.</strong></em></p>
<p><em>Edit: On the Windows 2000 box <a href="http://www.oldversion.com/Tiny_Personal_Firewall.html">Tiny Personal Firewall</a> was the best firewall I could find, it&rsquo;s lacking compared to <a href="http://www.obdev.at/products/littlesnitch/index.html">The Little Snitch</a> for Mac though.</em></p>
<h2 id="socks-proxy">Socks Proxy</h2>
<p>Spotify gives the option to send connections through a proxy server. This gave me an idea, what if we set up a proxy server that only allowed connections to .spotify.com and then forced Spotify to use it. (Of course, the proxy server would be useless for web-browsing or any other purpose.)</p>
<p>This took me a while to get working, in the end, I set up the <a href="http://www.inet.no/dante/">Dante proxy server</a> on one of my Debian servers. <a href="https://gist.github.com/4374654">Here&rsquo;s my /etc/danted.conf</a>, it&rsquo;s probably wrong and insecure so don&rsquo;t count on it. (10.10.10.0/24 is my internal subnet and eth1 happens to be the box&rsquo;s primary ethernet adapter. You will probably have to change those settings.)</p>
<p><img src="/images/blog/spotify-proxy.webp" alt="Spotify socks proxy settings."></p>
<p>After the proxy is set up, you need to tell Spotify to use it (I had to restart Spotify before it kicked in). But now we&rsquo;re set, we&rsquo;ve thrown all the bathwater out, but the baby&rsquo;s fine. This method has a lot of benefits too. It&rsquo;s a centralised solution and doesn&rsquo;t require any other software for the clients.</p>
<p><strong><em>Edit: Spotify ignored my proxy settings for P2P traffic! It sends all the good stuff through the proxy though, I then had to block any other traffic with a desktop firewall myself :(</em></strong></p>
<h2 id="hidden-option">Hidden option</h2>
<p>If I made Spotify, I would have probably added a secret option somewhere to disable P2P connections. It would be tricky to test the app without it, and surely Spotify&rsquo;s office would be brought to a standstill otherwise?! I&rsquo;ve got two ideas where they might have hidden the option:</p>
<p><img src="/images/blog/spotify-switches.webp" alt="Spotify command line switches"></p>
<p>Spotify seems to support command line switches, although they are undocumented there might be some interesting functionality hidden away and who knows maybe we can turn off sharing?</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;autologin_username&#34;</span> : <span style="color:#ba2121">&#34;REDACTED&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;listen_port&#34;</span> : <span style="color:#666">12517</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;cache_size&#34;</span> : <span style="color:#666">1024</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;proxy&#34;</span> : <span style="color:#ba2121">&#34;10.10.10.99:8080@socks4&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;proxy_mode&#34;</span> : <span style="color:#666">2</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;proxy_pass&#34;</span> : <span style="color:#ba2121">&#34;REDACTED&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;g_sett_high_bitrate&#34;</span> : <span style="color:#666">1</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;flash_version&#34;</span> : <span style="color:#666">9</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">&#34;realtime_clock_delta&#34;</span> : <span style="color:#666">0</span>
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>My final thought is editing the settings manually. Spotify has a settings file, it seems to be a simple JSON format so if we knew the names of hidden options we could change the settings there. Who knows maybe <code>'p2p': 0</code> is all that&rsquo;s standing in the way?!</p>
<p><strong>Either way, I have no idea where this elusive option is. Get searching and report back!</strong></p>
<h2 id="mess-up-the-cache">Mess up the cache</h2>
<p>Well, Spotify has a cache of files, presumably for use with the P2P sharing. If we could clear that cache or somehow disable it perhaps this could break P2P sharing?
The Spotify client only lets you reduce the cache size to a minimum of 1 Gigabyte but interestingly I&rsquo;ve noticed the setting is stored in the settings file. (Look at that sample above, &lsquo;&ldquo;cache_size&rdquo;: 1024&rsquo;). <strong>Perhaps this could be set to 0 manually? I don&rsquo;t have time to test that idea but let me know if it works for you.</strong></p>
<p><em>Edit: I&rsquo;ve just given it a try, I found that setting cache_size to 1 was lowest it would accept. It reduced my cache down to 90megs initially, and since then it&rsquo;s been dropping slowly. I&rsquo;m not sure if it will ever get as low as 1meg though. This idea might be counterproductive if it means we need to download more often, I&rsquo;m also not sure if it&rsquo;s reduced the P2P traffic or not. Another downside with this train of thought is it would be straightforward for the Spotify team to make the minimum number 1024 in a future update.</em></p>
<h2 id="alternative-client">Alternative Client</h2>
<p>I just had another thought, instead of forcing the official Spotify client to conform we could use an alternative client. There seems to be <a href="http://despotify.se/2010/02/11/open-source-spotify-clients-and-development-frameworks/">a range of alternative clients</a> available. I haven&rsquo;t tried any of them yet, but it looks like a promising approach.</p>
<p><strong>Cheers Dave.</strong></p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2010/10/06/fixing-spotify-uxtheme.dll-error-on-windows-2000</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2010/10/06/fixing-spotify-uxtheme.dll-error-on-windows-2000" />
    <title>Fixing Spotify uxtheme.dll error on Windows 2000</title>
    <published>2010-10-06T00:00:00+00:00</published>
    <updated>2010-10-06T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Spotify&rsquo;s recent update broke it for use on Windows 2000. It causes this error:</p>
<p><code>Cannot find 'UxTheme.dll'. Please, re-install this application</code></p>
<p>Anyway, I&rsquo;ve figured out a solution, no guarantees but this works for me. I&rsquo;ve cross-compiled the <a href="http://www.winehq.org/">Wine</a> implementation of the DLL for Windows. (I followed the <a href="http://wiki.winehq.org/CompilingDLLsUsingMingw">instructions here</a>.)</p>
<p><strong>Long story short <a href="/files/blog/win2000-uxthemedll.zip">download this zip</a> and extract it into c:\winnt\system32 .</strong></p>
<p>Hope that helps. Cheers, Dave.</p>
<p><em>Edit: Great this seems to be <a href="http://getsatisfaction.com/spotify/topics/uxtheme">working for other people</a> too.</em></p>
<p><em>Edit 2: The latest version (0.4.10) of Spotify has broken Windows 2000 support again. This time the client disappears immediately on launch with no errors or messages. Luckily though the Spotify team have figured out the problem and are fixing it in the next version, here&rsquo;s the message Magnus left on the support forum:</em></p>
<p><a href="http://getsatisfaction.com/spotify/topics/spotify_does_not_launch_since_the_latest_update"><img src="/images/blog/magnus-spotify-fix.webp" alt="Magnus Spotify fix explanation"></a></p>
<p><em>Edit 3: There&rsquo;s a <a href="http://www.spotify.com/uk/download/previews/">preview version available</a> that fixes the new problem.</em></p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2010/10/01/web.py-checkboxes</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2010/10/01/web.py-checkboxes" />
    <title>Web.py checkboxes</title>
    <published>2010-10-01T00:00:00+00:00</published>
    <updated>2010-10-01T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Checkboxes are a bit tricky with web.py, here&rsquo;s how to get them working:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">import</span> <span style="color:#00f;font-weight:bold">web</span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">from</span> <span style="color:#00f;font-weight:bold">web</span> <span style="color:#008000;font-weight:bold">import</span> form
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>example_form <span style="color:#666">=</span> form<span style="color:#666">.</span>Form(
</span></span><span style="display:flex;"><span>    form<span style="color:#666">.</span>Checkbox(<span style="color:#ba2121">&#34;lovelycheckbox&#34;</span>, description<span style="color:#666">=</span><span style="color:#ba2121">&#34;lovelycheckbox&#34;</span>, class_<span style="color:#666">=</span><span style="color:#ba2121">&#34;standard&#34;</span>, value<span style="color:#666">=</span><span style="color:#ba2121">&#34;something.. Anything!&#34;</span>),
</span></span><span style="display:flex;"><span>    form<span style="color:#666">.</span>Button(<span style="color:#ba2121">&#34;Update checkbox&#34;</span>, <span style="color:#008000">type</span><span style="color:#666">=</span><span style="color:#ba2121">&#34;submit&#34;</span>, description<span style="color:#666">=</span><span style="color:#ba2121">&#34;Send&#34;</span>),
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">class</span> <span style="color:#00f;font-weight:bold">grabresults</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">GET</span>(<span style="color:#008000">self</span>):
</span></span><span style="display:flex;"><span>        f <span style="color:#666">=</span> example_form()
</span></span><span style="display:flex;"><span>        <span style="color:#008000;font-weight:bold">return</span> f<span style="color:#666">.</span>render()
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">POST</span>(<span style="color:#008000">self</span>):
</span></span><span style="display:flex;"><span>        f <span style="color:#666">=</span> example_form()
</span></span><span style="display:flex;"><span>        theresult <span style="color:#666">=</span> f[<span style="color:#ba2121">&#39;lovelycheckbox&#39;</span>]<span style="color:#666">.</span>checked</span></span></code></pre></div>
<p>OK, so that&rsquo;s a dumb (and untested) example, but it gives you an idea. The &rsquo;theresult&rsquo; variable is going to be true or false depending on if the &rsquo;thelovelycheckbox&rsquo; was ticked or not.</p>
<p>Things to note:</p>
<ol>
<li>There&rsquo;s <a href="https://bugs.launchpad.net/webpy/+bug/585428">a web.py bug</a> which means that you have to give the checkbox a value attribute for them to work.</li>
<li>To get the result, you must use <code>.checked</code> instead of the normal <code>.value</code> attribute.</li>
</ol>
<p>Cheers, Dave.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2010/04/18/simple-compojure-project</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2010/04/18/simple-compojure-project" />
    <title>How to setup a simple Compojure project</title>
    <published>2010-04-18T00:00:00+00:00</published>
    <updated>2010-04-18T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Here&rsquo;s my attempt at setting up a basic website using the <a href="https://github.com/weavejester/compojure">Compojure</a> library. Thanks to <strong>arbscht</strong> in #compojure for showing me all of this.</p>
<p>So first up you need to create a new Leiningen project. Luckily there&rsquo;s an inbuilt command to help.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>lein new example
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> example</span></span></code></pre></div>
<p>Now you should be able to see the skeleton of your new project. Next, you need to set up the <code>project.clj</code> file, it&rsquo;s used to set the name and dependencies for the project.</p>
<p>You will need to add a description, adjust the version and add the required dependencies for a Compojure based website. Here&rsquo;s what an example:
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span>(<span style="color:#008000;font-weight:bold">defproject </span><span style="color:#19177c">example</span> <span style="color:#ba2121">&#34;0.0.1&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#19177c">:description</span> <span style="color:#ba2121">&#34;Test website to learn Compojure&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#19177c">:dependencies</span> [[<span style="color:#19177c">org.clojure/clojure</span> <span style="color:#ba2121">&#34;1.1.0&#34;</span>]
</span></span><span style="display:flex;"><span>                 [<span style="color:#19177c">org.clojure/clojure-contrib</span> <span style="color:#ba2121">&#34;1.1.0&#34;</span>]
</span></span><span style="display:flex;"><span>                 [<span style="color:#19177c">compojure</span> <span style="color:#ba2121">&#34;0.4.0-SNAPSHOT&#34;</span>]
</span></span><span style="display:flex;"><span>                 [<span style="color:#19177c">hiccup</span> <span style="color:#ba2121">&#34;0.2.3&#34;</span>]
</span></span><span style="display:flex;"><span>                 [<span style="color:#19177c">ring/ring-jetty-adapter</span> <span style="color:#ba2121">&#34;0.2.0&#34;</span>]
</span></span><span style="display:flex;"><span>                 [<span style="color:#19177c">leiningen/lein-swank</span> <span style="color:#ba2121">&#34;1.2.0-SNAPSHOT&#34;</span>]])</span></span></code></pre></div></p>
<p>Now inside ./src/example/ we need to create a <code>server.clj</code> file that we can use to setup the web server, here&rsquo;s mine:
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span>(<span style="color:#008000;font-weight:bold">ns </span><span style="color:#19177c">example.server</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">:use</span> <span style="color:#19177c">compojure.core</span>
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">ring.adapter.jetty</span>))
</span></span><span style="display:flex;"><span>(<span style="color:#00f">defroutes</span> <span style="color:#19177c">main-routes</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">GET</span> <span style="color:#ba2121">&#34;/&#34;</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#ba2121">&#34;&lt;h1&gt;Hello World&lt;/h1&gt;&#34;</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">GET</span> <span style="color:#ba2121">&#34;/test/:name&#34;</span> <span style="color:#19177c">req</span>
</span></span><span style="display:flex;"><span>       (<span style="color:#00f">test-page</span> (<span style="color:#19177c">:name</span> (<span style="color:#19177c">:params</span> <span style="color:#19177c">req</span>))))
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">ANY</span> <span style="color:#ba2121">&#34;*&#34;</span> []
</span></span><span style="display:flex;"><span>    {<span style="color:#19177c">:status</span> <span style="color:#666">404</span>, <span style="color:#19177c">:body</span> <span style="color:#ba2121">&#34;&lt;h1&gt;Page not found&lt;/h1&gt;&#34;</span>}))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000;font-weight:bold">defn </span><span style="color:#19177c">start</span> []
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">run-jetty</span> <span style="color:#666">#</span><span style="color:#19177c">&#39;main-routes</span> {<span style="color:#19177c">:port</span> <span style="color:#666">8080</span> <span style="color:#19177c">:join?</span> <span style="color:#19177c">false</span>}))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000;font-weight:bold">defn </span><span style="color:#19177c">test-page</span> [<span style="color:#19177c">name</span>]
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">str </span><span style="color:#ba2121">&#34;&lt;b&gt;Hello&#34;</span> <span style="color:#008000">name </span><span style="color:#ba2121">&#34;&lt;/b&gt;&#34;</span>))</span></span></code></pre></div></p>
<p>Three interesting things to note with that example:</p>
<ol>
<li>We put the <code>run-jetty</code> call inside a function. That way when the file is included the server doesn&rsquo;t automatically start. Inconvenient but it lets us <code>:reload</code> the file as much as we need.</li>
<li>We pass <code>run-jetty</code> <code>#'main-routes</code> instead of just <code>main-routes</code>, that is so that any changes to our routes are put in effect as soon as the <code>(defroutes main-routes ...)</code> s-exp is re-evaled.</li>
<li>The page /test/* is going to display &ldquo;Hello *&rdquo; as an example of how to capture &ldquo;parameters&rdquo; from the user. Try just passing <code>req</code> instead of <code>(:name (:params req))</code> to see what else is available to you.</li>
</ol>
<p>Now we are ready to give this a go, make sure you&rsquo;re in the root of your project and type:
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>lein deps
</span></span><span style="display:flex;"><span>lein swank</span></span></code></pre></div></p>
<p>To start a Clojure process for your project that also runs a Swank server. Then you need to open Emacs and do the following:
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span><span style="color:#19177c">M-x</span> <span style="color:#19177c">slime-connect</span> <span style="color:#19177c">ret</span> <span style="color:#19177c">ret</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">use</span> <span style="color:#19177c">&#39;example.server</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">example.server/start</span>)</span></span></code></pre></div></p>
<p>Now browse to <a href="http://localhost:8080">http://localhost:8080</a>, and you should see a &ldquo;Hello world&rdquo; page. Browse to <a href="http://localhost:8080/test/Dave">http://localhost:8080/test/Dave</a>, and it should say &ldquo;Hello Dave&rdquo;. Finally, change your server.clj to say &ldquo;Hello new world&rdquo; and re-eval the <code>(defroutes ...)</code>. The change should take effect as soon as you refresh your browser!</p>
<p>Cheers, Dave.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2010/04/07/clean-nginx-param-rewrite</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2010/04/07/clean-nginx-param-rewrite" />
    <title>Clean parameter based Nginx re-writes with map</title>
    <published>2010-04-07T00:00:00+00:00</published>
    <updated>2010-04-07T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>When I migrated to my new blog, I needed to re-write all the old WordPress URLs to point to the appropriate place. The problem was that standard rewrites aren&rsquo;t made based on GET parameters, so I was stuck.</p>
<p>Luckily Curly060 and Seph in #nginx gave me some ideas, and together we ended up with this:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Put this outside of server stanza
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span><span style="color:#008000;font-weight:bold">map</span> <span style="color:#19177c">$arg_cat</span> <span style="color:#19177c">$category</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">6</span> <span style="color:#ba2121">/blog/topic/security</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">11</span>    <span style="color:#ba2121">/blog/topic/mac</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">8.*</span>   <span style="color:#ba2121">/blog/topic/lisp</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">9.*</span>   <span style="color:#ba2121">/blog/topic/code</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">3</span>     <span style="color:#ba2121">/blog/topic/linux</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">14</span>    <span style="color:#ba2121">/blog/topic/thoughts</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">10</span>    <span style="color:#ba2121">/blog/topic/php</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">7</span>     <span style="color:#ba2121">/blog/topic/security</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">12</span>    <span style="color:#ba2121">/blog/topic/emacs</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">5</span>     <span style="color:#ba2121">/blog/topic/projects</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">9</span>     <span style="color:#ba2121">/blog/topic/code</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">13</span>    <span style="color:#ba2121">/blog/topic/python</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">1</span>     <span style="color:#ba2121">/blog</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">8</span>     <span style="color:#ba2121">/blog/topic/lisp</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Put this with other re-writes
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span><span style="color:#008000;font-weight:bold">if</span> <span style="color:#ba2121">(</span><span style="color:#19177c">$category</span><span style="color:#ba2121">)</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">rewrite</span> <span style="color:#ba2121">^</span> <span style="color:#19177c">$category</span>;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>If you want to do a permanent redirect, you have to clear all the arguments to prevent a redirect loop. The last stanza would look something like this:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">if</span> <span style="color:#ba2121">(</span><span style="color:#19177c">$category</span><span style="color:#ba2121">)</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">set</span> <span style="color:#19177c">$args</span> <span style="color:#ba2121">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#008000;font-weight:bold">rewrite</span> <span style="color:#ba2121">^</span> <span style="color:#19177c">$category</span> <span style="color:#ba2121">permanent</span>;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2010/03/04/rounded-corners-with-php</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2010/03/04/rounded-corners-with-php" />
    <title>Round image corners with PHP</title>
    <published>2010-03-04T00:00:00+00:00</published>
    <updated>2010-03-04T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I needed to round the corners of a banner image dynamically in PHP, and I couldn&rsquo;t find any explanations. Anyway, it turns out the easiest way is to make an image of a rounded corner, like this <a href="/files/blog/rounded-corner.png">rounded-corner.png</a> and then stamp it on all four corners. You have to rotate it each time of course. Anyway here&rsquo;s a snippet of code to give you an idea:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#666">&lt;?</span>php
</span></span><span style="display:flex;"><span><span style="color:#19177c">$banner</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;/path/to/banner-image.png&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#19177c">$corner</span> <span style="color:#666">=</span> <span style="color:#ba2121">&#39;/path/to/rounded-corner.png&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Load up the images
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span><span style="color:#19177c">$banner</span> <span style="color:#666">=</span> open_image(<span style="color:#19177c">$banner</span>);
</span></span><span style="display:flex;"><span><span style="color:#19177c">$corner</span> <span style="color:#666">=</span> open_image(<span style="color:#19177c">$corner</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Round the corner
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>imagecopy(<span style="color:#19177c">$banner</span>, <span style="color:#19177c">$corner</span>, <span style="color:#666">0</span>, <span style="color:#666">0</span>, <span style="color:#666">0</span>, <span style="color:#666">0</span>, imagesx(<span style="color:#19177c">$corner</span>), imagesy(<span style="color:#19177c">$corner</span>));
</span></span><span style="display:flex;"><span><span style="color:#19177c">$corner</span> <span style="color:#666">=</span> imagerotate(<span style="color:#19177c">$corner</span>, <span style="color:#666">90</span>, <span style="color:#666">0</span>);
</span></span><span style="display:flex;"><span>imagecopy(<span style="color:#19177c">$banner</span>, <span style="color:#19177c">$corner</span>, <span style="color:#666">0</span>, imagesy(<span style="color:#19177c">$banner</span>) <span style="color:#666">-</span> imagesy(<span style="color:#19177c">$corner</span>), <span style="color:#666">0</span>, <span style="color:#666">0</span>, imagesx(<span style="color:#19177c">$corner</span>), imagesy(<span style="color:#19177c">$corner</span>));
</span></span><span style="display:flex;"><span><span style="color:#19177c">$corner</span> <span style="color:#666">=</span> imagerotate(<span style="color:#19177c">$corner</span>, <span style="color:#666">90</span>, <span style="color:#666">0</span>);
</span></span><span style="display:flex;"><span>imagecopy(<span style="color:#19177c">$banner</span>, <span style="color:#19177c">$corner</span>, imagesx(<span style="color:#19177c">$banner</span>) <span style="color:#666">-</span> imagesy(<span style="color:#19177c">$corner</span>), imagesy(<span style="color:#19177c">$banner</span>) <span style="color:#666">-</span> imagesy(<span style="color:#19177c">$corner</span>), <span style="color:#666">0</span>, <span style="color:#666">0</span>, imagesx(<span style="color:#19177c">$corner</span>), imagesy(<span style="color:#19177c">$corner</span>));
</span></span><span style="display:flex;"><span><span style="color:#19177c">$corner</span> <span style="color:#666">=</span> imagerotate(<span style="color:#19177c">$corner</span>, <span style="color:#666">90</span>, <span style="color:#666">0</span>);
</span></span><span style="display:flex;"><span>imagecopy(<span style="color:#19177c">$banner</span>, <span style="color:#19177c">$corner</span>, imagesx(<span style="color:#19177c">$banner</span>) <span style="color:#666">-</span> imagesy(<span style="color:#19177c">$corner</span>), <span style="color:#666">0</span>, <span style="color:#666">0</span>, <span style="color:#666">0</span>, imagesx(<span style="color:#19177c">$corner</span>), imagesy(<span style="color:#19177c">$corner</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Output the image
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>header(<span style="color:#ba2121">&#34;Content-type: image/gif&#34;</span>);
</span></span><span style="display:flex;"><span>imagegif(<span style="color:#19177c">$banner</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">// Tidy up
</span></span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"></span>imagedestroy(<span style="color:#19177c">$banner</span>);
</span></span><span style="display:flex;"><span>imagedestroy(<span style="color:#19177c">$corner</span>);
</span></span><span style="display:flex;"><span><span style="color:#bc7a00">?&gt;</span><span style="">
</span></span></span></code></pre></div>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2009/07/14/web.py-captcha</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2009/07/14/web.py-captcha" />
    <title>web.py captcha form</title>
    <published>2009-07-14T00:00:00+00:00</published>
    <updated>2009-07-14T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I coded a basic captcha for my web.py form, here&rsquo;s how:</p>
<p>First I made a function that generates an image and returns it + the code (yea it&rsquo;s a mess).</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">import</span> <span style="color:#00f;font-weight:bold">Image</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">ImageDraw</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">ImageFont</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">cStringIO</span><span style="color:#666">,</span> <span style="color:#00f;font-weight:bold">random</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">getCaptcha</span>():
</span></span><span style="display:flex;"><span>    im <span style="color:#666">=</span> Image<span style="color:#666">.</span>new(<span style="color:#ba2121">&#34;RGB&#34;</span>, (<span style="color:#666">100</span>, <span style="color:#666">60</span>))
</span></span><span style="display:flex;"><span>    draw <span style="color:#666">=</span> ImageDraw<span style="color:#666">.</span>Draw(im)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">for</span> x <span style="color:#a2f;font-weight:bold">in</span> <span style="color:#008000">range</span>(<span style="color:#666">0</span>, <span style="color:#666">100</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#008000;font-weight:bold">for</span> y <span style="color:#a2f;font-weight:bold">in</span> <span style="color:#008000">range</span>(<span style="color:#666">0</span>, <span style="color:#666">60</span>):
</span></span><span style="display:flex;"><span>            draw<span style="color:#666">.</span>point((x, y), (<span style="color:#666">135</span>, <span style="color:#666">191</span>, <span style="color:#666">107</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    font <span style="color:#666">=</span> ImageFont<span style="color:#666">.</span>truetype(<span style="color:#ba2121">&#39;cracked.ttf&#39;</span>, <span style="color:#666">50</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    alphabet <span style="color:#666">=</span> <span style="color:#ba2121">&#39;abcdefghijklmnopqrstuvwxyz&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    word <span style="color:#666">=</span> <span style="color:#ba2121">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">for</span> i <span style="color:#a2f;font-weight:bold">in</span> <span style="color:#008000">range</span>(<span style="color:#666">5</span>):
</span></span><span style="display:flex;"><span>        word <span style="color:#666">=</span> word <span style="color:#666">+</span> alphabet[random<span style="color:#666">.</span>randint(<span style="color:#666">0</span>, <span style="color:#008000">len</span>(alphabet) <span style="color:#666">-</span><span style="color:#666">1</span>)]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    draw<span style="color:#666">.</span>text((<span style="color:#666">5</span>, <span style="color:#666">5</span>), word, font<span style="color:#666">=</span>font, fill<span style="color:#666">=</span>(<span style="color:#666">0</span>, <span style="color:#666">0</span>, <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    f <span style="color:#666">=</span> cStringIO<span style="color:#666">.</span>StringIO()
</span></span><span style="display:flex;"><span>    im<span style="color:#666">.</span>save(f, <span style="color:#ba2121">&#34;GIF&#34;</span>)
</span></span><span style="display:flex;"><span>    f<span style="color:#666">.</span>seek(<span style="color:#666">0</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">return</span> word, f</span></span></code></pre></div>
<p>Now here&rsquo;s the corresponding web.py code:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>urls <span style="color:#666">=</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ba2121">&#39;/captcha.gif&#39;</span>, <span style="color:#ba2121">&#39;captcha&#39;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">if</span> web<span style="color:#666">.</span>config<span style="color:#666">.</span>get(<span style="color:#ba2121">&#39;_session&#39;</span>) <span style="color:#a2f;font-weight:bold">is</span> <span style="color:#008000;font-weight:bold">None</span>:
</span></span><span style="display:flex;"><span>    session <span style="color:#666">=</span> web<span style="color:#666">.</span>session<span style="color:#666">.</span>Session(app, web<span style="color:#666">.</span>session<span style="color:#666">.</span>DiskStore(<span style="color:#ba2121">&#39;sessions&#39;</span>), initializer<span style="color:#666">=</span>{<span style="color:#ba2121">&#39;captcha&#39;</span>: <span style="color:#ba2121">&#39;&#39;</span>})
</span></span><span style="display:flex;"><span>    web<span style="color:#666">.</span>config<span style="color:#666">.</span>_session <span style="color:#666">=</span> session
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">else</span>:
</span></span><span style="display:flex;"><span>    session <span style="color:#666">=</span> web<span style="color:#666">.</span>config<span style="color:#666">.</span>_session
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>vcaptcha <span style="color:#666">=</span> form<span style="color:#666">.</span>Validator(<span style="color:#ba2121">&#39;Please enter the code&#39;</span>,  <span style="color:#008000;font-weight:bold">lambda</span> x:x <span style="color:#666">==</span> session<span style="color:#666">.</span>captcha)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>enquiry_form <span style="color:#666">=</span> form<span style="color:#666">.</span>Form(
</span></span><span style="display:flex;"><span>    <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>    form<span style="color:#666">.</span>Textbox(<span style="color:#ba2121">&#34;captcha&#34;</span>, vcaptcha, description<span style="color:#666">=</span><span style="color:#ba2121">&#34;Validation Code&#34;</span>, pre<span style="color:#666">=</span><span style="color:#ba2121">&#34;&lt;img src=&#39;/captcha.gif&#39; valign=center&gt;&lt;br&gt;&#34;</span>, class_<span style="color:#666">=</span><span style="color:#ba2121">&#34;standard&#34;</span>, style<span style="color:#666">=</span><span style="color:#ba2121">&#34;width:70px;&#34;</span>),
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">class</span> <span style="color:#00f;font-weight:bold">captcha</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">GET</span>(<span style="color:#008000">self</span>):
</span></span><span style="display:flex;"><span>        web<span style="color:#666">.</span>header(<span style="color:#ba2121">&#34;Content-Type&#34;</span>, <span style="color:#ba2121">&#34;image/gif&#34;</span>)
</span></span><span style="display:flex;"><span>        captcha <span style="color:#666">=</span> getCaptcha()
</span></span><span style="display:flex;"><span>        session<span style="color:#666">.</span>captcha <span style="color:#666">=</span> captcha[<span style="color:#666">0</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#008000;font-weight:bold">return</span> captcha[<span style="color:#666">1</span>]<span style="color:#666">.</span>read()</span></span></code></pre></div>
<p>To make the form look prettier, I also modified web.py to give the table rows an id and then used CSS to alter their padding etc.</p>
<p>Sorry, I didn&rsquo;t take much time over this post. Hopefully, the code will get you started though, Dave.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2009/07/10/i-like-python</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2009/07/10/i-like-python" />
    <title>I like Python</title>
    <published>2009-07-10T00:00:00+00:00</published>
    <updated>2009-07-10T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I decided to learn Python and Django in the hope that it would be a halfway house between PHP and Lisp, letting me get stuff done while not being PHP.</p>
<p>My first impression of Django was pretty negative. I bought the &lsquo;definitive guide&rsquo; book only to find it was not so much definitive as obsolete. Also, the amount of arbitrary-seeming magic didn&rsquo;t impress. Having said that I have passed through that and I&rsquo;m beginning to get the hang of it.</p>
<p>Python, on the other hand, was different, to start with I HATED the look of the code compared to Lisp but after a few hours hacking it doesn&rsquo;t seem that bad. For my first program, I decided to <a href="http://www.craigmurphy.com/blog/?p=1417">solve this little puzzle</a>. <a href="http://diveintopython.org/">This excellent guide</a> got me going quickly and along with a few small pointers from <strong>verte</strong> in the friendly-seeming #python I came to a solution.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">def</span> <span style="color:#00f">draw_diamond</span>(letter):
</span></span><span style="display:flex;"><span>    alphabet <span style="color:#666">=</span> <span style="color:#ba2121">&#39;abcdefghijklmnopqrstuvqxyz&#39;</span>
</span></span><span style="display:flex;"><span>    size <span style="color:#666">=</span> alphabet<span style="color:#666">.</span>find(letter)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#008000;font-weight:bold">for</span> x <span style="color:#a2f;font-weight:bold">in</span> <span style="color:#008000">range</span>(size) <span style="color:#666">+</span> <span style="color:#008000">range</span>(size, <span style="color:#666">-</span><span style="color:#666">1</span>, <span style="color:#666">-</span><span style="color:#666">1</span>):
</span></span><span style="display:flex;"><span>        line <span style="color:#666">=</span> [<span style="color:#ba2121">&#39; &#39;</span>] <span style="color:#666">*</span> ((<span style="color:#666">2</span> <span style="color:#666">*</span> size) <span style="color:#666">+</span> <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>        line[size <span style="color:#666">+</span> x] <span style="color:#666">=</span> alphabet[x]
</span></span><span style="display:flex;"><span>        line[size <span style="color:#666">-</span> x] <span style="color:#666">=</span> alphabet[x]
</span></span><span style="display:flex;"><span>        <span style="color:#008000">print</span> <span style="color:#ba2121">&#34;&#34;</span><span style="color:#666">.</span>join(line)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>draw_diamond(<span style="color:#ba2121">&#39;d&#39;</span>)</span></span></code></pre></div>
<p>It didn&rsquo;t take long, the code looks nice and most importantly I enjoyed writing it. I&rsquo;m pretty impressed!</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2009/07/10/web.py-tutorial-sqlite</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2009/07/10/web.py-tutorial-sqlite" />
    <title>web.py &#43; SQLite tutorial</title>
    <published>2009-07-10T00:00:00+00:00</published>
    <updated>2009-07-10T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>If you want to follow the web.py tutorial using SQLite here&rsquo;s how:</p>
<p>The line for your Python to connect to DB:</p>
<p><div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>db <span style="color:#666">=</span> web<span style="color:#666">.</span>database(dbn<span style="color:#666">=</span><span style="color:#ba2121">&#39;sqlite&#39;</span>, db<span style="color:#666">=</span><span style="color:#ba2121">&#39;testdb&#39;</span>)</span></span></code></pre></div>
To create the database type <code>sqlite3 testdb</code>
and input this SQL:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">CREATE</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">TABLE</span><span style="color:#bbb"> </span>todo<span style="color:#bbb"> </span>(id<span style="color:#bbb"> </span><span style="color:#008000">integer</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">primary</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">key</span>,<span style="color:#bbb"> </span>title<span style="color:#bbb"> </span><span style="color:#008000">text</span>,<span style="color:#bbb"> </span>created<span style="color:#bbb"> </span><span style="color:#008000">date</span>,<span style="color:#bbb"> </span>done<span style="color:#bbb"> </span><span style="color:#008000">boolean</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">default</span><span style="color:#bbb"> </span><span style="color:#ba2121">&#39;f&#39;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#008000;font-weight:bold">CREATE</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">TRIGGER</span><span style="color:#bbb"> </span>insert_todo_created<span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">after</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">insert</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">on</span><span style="color:#bbb"> </span>todo<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#008000;font-weight:bold">begin</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#008000;font-weight:bold">update</span><span style="color:#bbb"> </span>todo<span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">set</span><span style="color:#bbb"> </span>created<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>datetime(<span style="color:#ba2121">&#39;now&#39;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#008000;font-weight:bold">where</span><span style="color:#bbb"> </span>rowid<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">new</span>.rowid;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#008000;font-weight:bold">end</span>;</span></span></code></pre></div>
<p>Now create an entry with this SQL</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold">insert</span><span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">into</span><span style="color:#bbb"> </span>todo<span style="color:#bbb"> </span>(title)<span style="color:#bbb"> </span><span style="color:#008000;font-weight:bold">values</span><span style="color:#bbb"> </span>(<span style="color:#ba2121">&#39;Learn web.py&#39;</span>);</span></span></code></pre></div>
<p>Finally, quit sqlite3 when you&rsquo;re ready by typing <code>.quit</code></p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2009/06/23/meld-darwinports-problem</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2009/06/23/meld-darwinports-problem" />
    <title>Meld Darwinports problem</title>
    <published>2009-06-23T00:00:00+00:00</published>
    <updated>2009-06-23T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>If you are having a problem getting the Darwinports package of the <a href="http://meldmerge.org/">Meld visual diff tool</a> running on your Mac try these two commands before re-running the program.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo launchctl load -w /Library/LaunchDaemons/org.freedesktop.dbus-system.plist
</span></span><span style="display:flex;"><span>sudo launchctl load -w /Library/LaunchAgents/org.freedesktop.dbus-session.plist</span></span></code></pre></div>
<p>The error I was getting before was this:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-console" data-lang="console"><span style="display:flex;"><span><span style="color:#888">Xlib: extension &#34;RANDR&#34; missing on display &#34;/tmp/launch-RsRIqw/:0&#34;.
</span></span></span><span style="display:flex;"><span><span style="color:#888">Dynamic session lookup supported but failed: launchd did not provide a socket path, verify that org.freedesktop.dbus-session.plist is loaded!
</span></span></span><span style="display:flex;"><span><span style="color:#888">Dynamic session lookup supported but failed: launchd did not provide a socket path, verify that org.freedesktop.dbus-session.plist is loaded!
</span></span></span><span style="display:flex;"><span><span style="color:#888">Dynamic session lookup supported but failed: launchd did not provide a socket path, verify that org.freedesktop.dbus-session.plist is loaded!
</span></span></span><span style="display:flex;"><span><span style="color:#888">Dynamic session lookup supported but failed: launchd did not provide a socket path, verify that org.freedesktop.dbus-session.plist is loaded!
</span></span></span><span style="display:flex;"><span><span style="color:#888">Dynamic session lookup supported but failed: launchd did not provide a socket path, verify that org.freedesktop.dbus-session.plist is loaded!
</span></span></span><span style="display:flex;"><span><span style="color:#888">Dynamic session lookup supported but failed: launchd did not provide a socket path, verify that org.freedesktop.dbus-session.plist is loaded!
</span></span></span><span style="display:flex;"><span><span style="color:#888">Traceback (most recent call last):
</span></span></span><span style="display:flex;"><span><span style="color:#888">File &#34;/opt/local/bin/meld&#34;, line 109, in &lt;module&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#888">meldapp.main()
</span></span></span><span style="display:flex;"><span><span style="color:#888">File &#34;/opt/local/lib/meld/meldapp.py&#34;, line 855, in main
</span></span></span><span style="display:flex;"><span><span style="color:#888">app = MeldApp()
</span></span></span><span style="display:flex;"><span><span style="color:#888">File &#34;/opt/local/lib/meld/meldapp.py&#34;, line 528, in __init__
</span></span></span><span style="display:flex;"><span><span style="color:#888">self.prefs = MeldPreferences()
</span></span></span><span style="display:flex;"><span><span style="color:#888">File &#34;/opt/local/lib/meld/meldapp.py&#34;, line 465, in __init__
</span></span></span><span style="display:flex;"><span><span style="color:#888">prefs.Preferences.__init__(self, &#34;/apps/meld&#34;, self.defaults)
</span></span></span><span style="display:flex;"><span><span style="color:#888">File &#34;/opt/local/lib/meld/prefs.py&#34;, line 91, in __init__
</span></span></span><span style="display:flex;"><span><span style="color:#888">self._gconf.add_dir(rootkey, gconf.CLIENT_PRELOAD_NONE)
</span></span></span><span style="display:flex;"><span><span style="color:#888">glib.GError: Failed to contact configuration server; some possible causes are that you need to enable TCP/IP networking for ORBit, or you have stale NFS locks due to a system crash. See http://projects.gnome.org/gconf/ for information. (Details - 1: Failed to get connection to session: Not enough memory)
</span></span></span><span style="display:flex;"><span><span style="color:#888">&lt;/module&gt;
</span></span></span></code></pre></div>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/12/26/lisp-couchdb-cl-couch-example</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/12/26/lisp-couchdb-cl-couch-example" />
    <title>Lisp, CouchDB and cl-couch example usage</title>
    <published>2008-12-26T00:00:00+00:00</published>
    <updated>2008-12-26T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I have been reading up on <a href="http://couchdb.apache.org/index.html">CouchDB</a>, an interesting alternative to using relational databases for (usually) completely the wrong purpose. After the last contract I have been working on and having to deal with the mess that led to I can really see the need for tools like this.</p>
<p>I always thought it was weird how you ended up having to do loads of queries per page viewed, how you are limited by SQL when doing anything more complex things and buggered when it came to storing trees or other structures. With CouchDB queries are prepared in advance using proper programming languages and are called views, this lets you grab stuff with a lot more power. It also offers much more flexibility with how you structure your data. (You can have a query that ignores records that aren&rsquo;t relevant for example.)</p>
<p><a href="http://books.couchdb.org/">A free online book</a>, friendly IRC channel and <a href="http://wiki.apache.org/couchdb/Cl-Couch">a great Lisp library</a> are some of the bonuses to what already looks like a <a href="http://video.yahoo.com/watch/2278623/7162319">pretty good idea</a>.</p>
<p>Another cool aspect of CouchDB is that it gives you your data in JSON form so you can skip the &lsquo;PHP Layer&rsquo; and <a href="http://jchris.mfdz.com:5984/blogdb/_design%2Fcouchdb-example-blog/index.html">make a dynamic application with just HTML, JavaScript and CouchDB</a>! (View the source of that page to see!)</p>
<p>I am going to write a couple of basic programs to get the hang of CouchDB soon, but before I started, I needed to get a few examples working. It took me a while so to save you the bother here is the code:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#19177c">asdf:oos</span> <span style="color:#19177c">&#39;asdf:load-op</span> <span style="color:#19177c">:cl-couch</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Setup the blog package</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defpackage</span> <span style="color:#19177c">:blog</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">:use</span> <span style="color:#19177c">:asdf</span> <span style="color:#19177c">:asdf-install</span> <span style="color:#19177c">:cl</span> <span style="color:#19177c">:hunchentoot</span> <span style="color:#19177c">:cl-who</span> <span style="color:#19177c">:cl-couchdb-client</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">:shadowing-import-from</span> <span style="color:#19177c">#:cl-couchdb-client</span> <span style="color:#19177c">#:url-encode</span> <span style="color:#19177c">:url-decode</span>))
</span></span><span style="display:flex;"><span>(<span style="color:#008000">in-package</span> <span style="color:#19177c">:blog</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Connect to CouchDB</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">open-server</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Create a database</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">couch-request</span> <span style="color:#19177c">:put</span> (<span style="color:#19177c">contacts</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Add a few records</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">couch-request</span> <span style="color:#19177c">:post</span> (<span style="color:#19177c">contacts</span>) <span style="color:#666">&#39;</span>((<span style="color:#ba2121">&#34;Name&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Simon&#34;</span>)
</span></span><span style="display:flex;"><span>                                  (<span style="color:#ba2121">&#34;Skype&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;simon@skype.com&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">couch-request</span> <span style="color:#19177c">:post</span> (<span style="color:#19177c">contacts</span>) <span style="color:#666">&#39;</span>((<span style="color:#ba2121">&#34;Name&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Hozay Jones&#34;</span>)
</span></span><span style="display:flex;"><span>                                  (<span style="color:#ba2121">&#34;Email&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;hozay@gmail.com&#34;</span>)
</span></span><span style="display:flex;"><span>                                  (<span style="color:#ba2121">&#34;Address&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;123 test st&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">couch-request</span> <span style="color:#19177c">:post</span> (<span style="color:#19177c">contacts</span>) <span style="color:#666">&#39;</span>((<span style="color:#ba2121">&#34;Name&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Dave&#34;</span>) (<span style="color:#ba2121">&#34;Website&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;http://kzar.co.uk&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">couch-request</span> <span style="color:#19177c">:post</span> (<span style="color:#19177c">contacts</span>) <span style="color:#666">&#39;</span>((<span style="color:#ba2121">&#34;Crap&#34;</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Blah&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; List the records</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">couch-request</span> <span style="color:#19177c">:get</span> (<span style="color:#19177c">contacts</span> <span style="color:#19177c">_all_docs</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Now using futon create a view for the contacts database</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Here is the map JavaScript function to use:</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;function(doc) {</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;  if (doc.Name) {</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;    emit(doc.Name, doc);</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;  }</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Call the design document contacts and the view by_name</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Now we can use this view to see all the records with a Name</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">couch-request</span> <span style="color:#19177c">:get</span> (<span style="color:#19177c">contacts/_view/contacts/by_name</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; And now any with the name Simon</span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">couch-request</span> <span style="color:#19177c">:get</span> (<span style="color:#19177c">contacts/_view/contacts/by_name</span> <span style="color:#19177c">:key</span> <span style="color:#ba2121">&#34;\&#34;Simon\&#34;&#34;</span>))</span></span></code></pre></div>
<p>(I am brand new to this so I might have made some mistakes, it does give you somewhere to start from though.)</p>
<p>Any feedback would be great,</p>
<p>Cheers, Dave.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/12/21/string-permutations-using-lisp</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/12/21/string-permutations-using-lisp" />
    <title>String permutations using Lisp</title>
    <published>2008-12-21T00:00:00+00:00</published>
    <updated>2008-12-21T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Someone in #lisp asked about permutations the other day. I hadn&rsquo;t come across them before so after looking up what they where I decided it sounded like a good programming exercise to try and find all the permutations for a given string.</p>
<p>Anyway, it took me a little while to figure out how to do it but once I did it wasn&rsquo;t so hard, here&rsquo;s the code:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">transpose</span> (<span style="color:#00f;font-weight:bold">string</span> <span style="color:#00f">position</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return string with the characters at position transposed&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">before</span> (<span style="color:#008000">when</span> (<span style="color:#00f">&gt;</span> <span style="color:#00f">position</span> <span style="color:#666">0</span>)
</span></span><span style="display:flex;"><span>                  (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#666">0</span> <span style="color:#00f">position</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">after</span> (<span style="color:#008000">when</span> (<span style="color:#00f">&lt;</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#00f">position</span>) (<span style="color:#00f">length</span> <span style="color:#00f;font-weight:bold">string</span>))
</span></span><span style="display:flex;"><span>                 (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> (<span style="color:#00f">+</span> <span style="color:#666">2</span> <span style="color:#00f">position</span>)))))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">concatenate</span> <span style="color:#19177c">&#39;string</span> <span style="color:#19177c">before</span>
</span></span><span style="display:flex;"><span>                 (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> (<span style="color:#00f">+</span> <span style="color:#00f">position</span> <span style="color:#666">1</span>) (<span style="color:#00f">+</span> <span style="color:#00f">position</span> <span style="color:#666">2</span>))
</span></span><span style="display:flex;"><span>                 (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#00f">position</span> (<span style="color:#00f">+</span> <span style="color:#00f">position</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>                 <span style="color:#19177c">after</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">permutations-helper</span> (<span style="color:#19177c">permutations</span> <span style="color:#00f">position</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Does the hard work for the permutations function.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">cond</span> ((<span style="color:#00f">not</span> <span style="color:#19177c">permutations</span>) <span style="color:#800">nil</span>)
</span></span><span style="display:flex;"><span>        ((<span style="color:#00f">eql</span> (<span style="color:#00f">length</span> (<span style="color:#00f">car</span> <span style="color:#19177c">permutations</span>)) (<span style="color:#00f">+</span> <span style="color:#00f">position</span> <span style="color:#666">1</span>)) <span style="color:#19177c">permutations</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#800">t</span> (<span style="color:#19177c">permutations-helper</span>
</span></span><span style="display:flex;"><span>            (<span style="color:#00f">append</span> <span style="color:#19177c">permutations</span> (<span style="color:#00f">mapcar</span> (<span style="color:#008000">lambda</span> (<span style="color:#19177c">x</span>) (<span style="color:#19177c">transpose</span> <span style="color:#19177c">x</span> <span style="color:#00f">position</span>)) <span style="color:#19177c">permutations</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#00f">position</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">permutations</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a string and return a list of it&#39;s permutations&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">remove-duplicates</span> (<span style="color:#19177c">permutations-helper</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#00f;font-weight:bold">string</span>) <span style="color:#666">0</span>) <span style="color:#19177c">:test</span> <span style="color:#00f">#&#39;equal</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">permutations</span> <span style="color:#ba2121">&#34;population&#34;</span>)</span></span></code></pre></div>
<p>Any feedback would be great, feel free to use the code.</p>
<p>Cheers, Dave.</p>
<hr>
<p>Edit: Before I re-wrote my blog there was a great comment from <strong>Kelsar</strong> that corrected the above code. I&rsquo;ve included it below so anyone reading this has a working example to peek at and to help demonstrate what&rsquo;s wrong with the above example.</p>
<blockquote>
There is something wrong in your program; for example: <code>(member "trebor" (permutations "robert")) -> NIL</code>
<p>You probably need something like &ldquo;rotation&rdquo; of string. This is my version (on lists, not strings, but this is the same):</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;utility function</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">mappend</span> (<span style="color:#19177c">fn</span> <span style="color:#19177c">lst</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">apply</span> <span style="color:#00f">#&#39;append</span> (<span style="color:#00f">mapcar</span> <span style="color:#19177c">fn</span> <span style="color:#19177c">lst</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;rotation</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">rot</span> (<span style="color:#19177c">lst</span>) <span style="color:#666">`</span>(<span style="color:#666">,@</span>(<span style="color:#00f">cdr</span> <span style="color:#19177c">lst</span>) <span style="color:#666">,</span>(<span style="color:#00f">car</span> <span style="color:#19177c">lst</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">full-rot</span> (<span style="color:#19177c">lst</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">bf</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">lst</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">do</span> ((<span style="color:#19177c">x</span> (<span style="color:#19177c">rot</span> <span style="color:#19177c">lst</span>) (<span style="color:#19177c">rot</span> <span style="color:#19177c">x</span>))) ((<span style="color:#00f">equal</span> <span style="color:#19177c">x</span> <span style="color:#19177c">lst</span>))
</span></span><span style="display:flex;"><span>      (<span style="color:#008000">push</span> <span style="color:#19177c">x</span> <span style="color:#19177c">bf</span>)) <span style="color:#19177c">bf</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;;permutations</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">permutation</span> (<span style="color:#19177c">lst</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f;font-weight:bold">null</span> <span style="color:#19177c">lst</span>) <span style="color:#666">&#39;</span>(())
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">mappend</span> <span style="color:#00f">#&#39;</span>(<span style="color:#008000">lambda</span> (<span style="color:#19177c">x</span>) (<span style="color:#19177c">full-rot</span> (<span style="color:#00f;font-weight:bold">cons</span> (<span style="color:#00f">car</span> <span style="color:#19177c">lst</span>) <span style="color:#19177c">x</span>))) (<span style="color:#19177c">permutation</span> (<span style="color:#00f">cdr</span> <span style="color:#19177c">lst</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">permutation</span> <span style="color:#666">&#39;</span>(<span style="color:#666">1</span> <span style="color:#666">2</span> <span style="color:#666">3</span> <span style="color:#666">4</span>))</span></span></code></pre></div>
<p>greetings, kelsar</p>
</blockquote>
***
<p>Edit 2: This old post caught my eye, the year is now 2015, and it&rsquo;s been quite a while since I&rsquo;ve done any Lisp! Here&rsquo;s a quick version of Kelsar&rsquo;s algorithm rewritten in Clojure:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span>(<span style="color:#008000;font-weight:bold">defn </span><span style="color:#19177c">rotations</span> [<span style="color:#19177c">s</span>]
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let </span>[<span style="color:#19177c">length</span> (<span style="color:#008000">count </span><span style="color:#19177c">s</span>)]
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">take </span><span style="color:#19177c">length</span> (<span style="color:#00f">partition</span> <span style="color:#19177c">length</span> <span style="color:#666">1</span> (<span style="color:#008000">cycle </span><span style="color:#19177c">s</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000;font-weight:bold">defn </span><span style="color:#19177c">permutations</span> [<span style="color:#19177c">s</span>]
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if </span>(<span style="color:#008000">seq </span><span style="color:#19177c">s</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">mapcat </span><span style="color:#666">#</span>(<span style="color:#00f">rotations</span> (<span style="color:#008000">cons </span>(<span style="color:#008000">first </span><span style="color:#19177c">s</span>) <span style="color:#19177c">%</span>)) (<span style="color:#00f">permutations</span> (<span style="color:#008000">rest </span><span style="color:#19177c">s</span>)))
</span></span><span style="display:flex;"><span>    [[]]))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">map </span><span style="color:#666">#</span>(<span style="color:#008000">apply str </span><span style="color:#19177c">%</span>) (<span style="color:#00f">permutations</span> <span style="color:#ba2121">&#34;robert&#34;</span>))</span></span></code></pre></div>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/12/18/website-change-alerts-lisp</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/12/18/website-change-alerts-lisp" />
    <title>Website change alerts using Lisp</title>
    <published>2008-12-18T00:00:00+00:00</published>
    <updated>2008-12-18T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>A while ago I posted about my horrible scraper code and about how I was too embarrassed to share it&hellip;</p>
<p>Well, it worked well! It emailed me nice &ldquo;HTML diff&rsquo;s&rdquo; of how the page was changing, and it successfully notified me when the visa was open for applications.</p>
<p>I have decided to post it so that if someone wants to write a scraper/emailer in Lisp at least, they have an example. (I couldn&rsquo;t find many examples of how to use a lot of these libraries to start with which made it quite hard.)</p>
<p>So excuse the mess, here&rsquo;s the code:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#19177c">asdf:operate</span> <span style="color:#19177c">&#39;asdf:compile-op</span> <span style="color:#19177c">:curl</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">asdf:operate</span> <span style="color:#19177c">&#39;asdf:load-op</span> <span style="color:#19177c">:curl</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#00f">require</span> <span style="color:#19177c">:cl-html-parse</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#00f">require</span> <span style="color:#19177c">:htmlgen</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#00f">require</span> <span style="color:#19177c">:cl-html-diff</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#00f">require</span> <span style="color:#19177c">:cl-smtp</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; TODO</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Fix logic so that it doesn&#39;t go crazy when the page / selection is empty</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Get rid of those horrible changed, same and new functions</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Get rid of email list at the top</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Tidy up the send mail function</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; Settings</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defparameter</span> <span style="color:#19177c">*mailserver*</span> <span style="color:#ba2121">&#34;localhost&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defparameter</span> <span style="color:#19177c">*emails*</span> <span style="color:#666">&#39;</span>(<span style="color:#ba2121">&#34;kzar@kzar.co.uk&#34;</span> <span style="color:#ba2121">&#34;fredbob@gmail.com&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defparameter</span> <span style="color:#19177c">*store*</span> <span style="color:#ba2121">&#34;/home/kzar/scrape/&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Where all the temp files are saved.&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defparameter</span> <span style="color:#19177c">*log-file*</span> <span style="color:#ba2121">&#34;/var/log/visa-check.log&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; Workings</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*branches-above*</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">parse-html</span> (<span style="color:#19177c">html</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take some HTML and return a tree.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">NET.HTML.PARSER:parse-html</span> <span style="color:#19177c">html</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">craft-html</span> (<span style="color:#19177c">tree</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a tree and return some HTML&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">with-output-to-string</span> (<span style="color:#00f;font-weight:bold">stream</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">net.html.generator:html-print</span> <span style="color:#19177c">tree</span> <span style="color:#00f;font-weight:bold">stream</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">load-file</span> (<span style="color:#19177c">filename</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Load the file given by filename and and return the contents as a string&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">with-open-file</span> (<span style="color:#19177c">in</span> <span style="color:#19177c">filename</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">with-standard-io-syntax</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">seq</span> (<span style="color:#00f">make-string</span> (<span style="color:#00f">file-length</span> <span style="color:#19177c">in</span>))))
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">read-sequence</span> <span style="color:#19177c">seq</span> <span style="color:#19177c">in</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">seq</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">load-html</span> (<span style="color:#19177c">filename</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Load a HTML file and parse it into s-expressions&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">parse-html</span> (<span style="color:#19177c">load-file</span> <span style="color:#19177c">filename</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">save-string</span> (<span style="color:#19177c">filename</span> <span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Write the string to filename (overwriting anything that might already be there).&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">with-open-file</span> (<span style="color:#19177c">out</span> <span style="color:#19177c">filename</span>
</span></span><span style="display:flex;"><span>                       <span style="color:#19177c">:direction</span> <span style="color:#19177c">:output</span>
</span></span><span style="display:flex;"><span>                       <span style="color:#19177c">:if-exists</span> <span style="color:#19177c">:supersede</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">with-standard-io-syntax</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">write-string</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">out</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">save-html</span> (<span style="color:#19177c">filename</span> <span style="color:#19177c">html</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Save some s-expressions to HTML&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">save-string</span> <span style="color:#19177c">filename</span> (<span style="color:#19177c">craft-html</span> <span style="color:#19177c">html</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">append-string</span> (<span style="color:#19177c">filename</span> <span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Append the string to the end of the file given by filename&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">with-open-file</span> (<span style="color:#19177c">out</span> <span style="color:#19177c">filename</span>
</span></span><span style="display:flex;"><span>                       <span style="color:#19177c">:direction</span> <span style="color:#19177c">:output</span>
</span></span><span style="display:flex;"><span>                       <span style="color:#19177c">:if-exists</span> <span style="color:#19177c">:append</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">with-standard-io-syntax</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">write-string</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">out</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">time-stamp</span> ()
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return a nicely formatted timestamp of the current date / time&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">multiple-value-bind</span> (<span style="color:#00f">second</span> <span style="color:#19177c">minute</span> <span style="color:#19177c">hour</span> <span style="color:#19177c">day</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">decode-universal-time</span> (<span style="color:#00f">get-universal-time</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;~A/~A/~A ~A:~A:~A&#34;</span> <span style="color:#19177c">day</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span> <span style="color:#19177c">hour</span> <span style="color:#19177c">minute</span> <span style="color:#00f">second</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">log-message</span> (<span style="color:#19177c">message</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Append a log entry to the *log* file, including a timestamp&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">append-string</span> <span style="color:#19177c">*log-file*</span> (<span style="color:#00f">concatenate</span> <span style="color:#19177c">&#39;string</span> (<span style="color:#19177c">time-stamp</span>) <span style="color:#ba2121">&#34;: &#34;</span> <span style="color:#19177c">message</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">file-exists?</span> (<span style="color:#19177c">filename</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return T if the filename exists, nil otherwise&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">with-open-file</span> (<span style="color:#19177c">in</span> <span style="color:#19177c">filename</span> <span style="color:#19177c">:if-does-not-exist</span> <span style="color:#800">nil</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">in</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#800">t</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">download</span> (<span style="color:#19177c">URL</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Download the contents of the URL and return it&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">curl:with-connection-returning-string</span> (<span style="color:#19177c">:cookies</span> <span style="color:#800">nil</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">curl:set-option</span> <span style="color:#19177c">:url</span> <span style="color:#19177c">URL</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">curl:perform</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">strip-utf</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return string minus any UTF characters.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">remove-if</span> (<span style="color:#008000">lambda</span> (<span style="color:#19177c">x</span>) (<span style="color:#00f">&gt;</span> (<span style="color:#00f">char-code</span> <span style="color:#19177c">x</span>) <span style="color:#666">127</span>))
</span></span><span style="display:flex;"><span>             <span style="color:#00f;font-weight:bold">string</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">send-email</span> (<span style="color:#19177c">from</span> <span style="color:#19177c">to</span> <span style="color:#19177c">subject</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Use cl-smtp to send an email.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">cl-smtp:send-email</span>
</span></span><span style="display:flex;"><span>   <span style="color:#19177c">*mailserver*</span>
</span></span><span style="display:flex;"><span>   <span style="color:#19177c">from</span>
</span></span><span style="display:flex;"><span>   <span style="color:#19177c">to</span>
</span></span><span style="display:flex;"><span>   <span style="color:#19177c">subject</span>
</span></span><span style="display:flex;"><span>   (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;&lt;style type=\&#34;text/css\&#34;&gt;ins {background: #bfb} del{background: #fcc} ins,del {text-decoration: none}&lt;/style&gt;~A&#34;</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>   <span style="color:#19177c">:extra-headers</span> <span style="color:#666">&#39;</span>((<span style="color:#ba2121">&#34;Content-type&#34;</span> <span style="color:#ba2121">&#34;text/html; charset=\&#34;iso-8859-1\&#34;&#34;</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">spam</span> (<span style="color:#19177c">from</span> <span style="color:#19177c">subject</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Spam everyone in *emails* the email given by the parameters&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">dolist</span> (<span style="color:#19177c">email</span> <span style="color:#19177c">*emails*</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">send-email</span> <span style="color:#19177c">from</span> <span style="color:#19177c">email</span> <span style="color:#19177c">subject</span> <span style="color:#19177c">body</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">list-if</span> (<span style="color:#19177c">foo</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;If foo&#39;s not nil return (list foo).&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> <span style="color:#19177c">foo</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">foo</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">find-item</span> (<span style="color:#19177c">item</span> <span style="color:#00f;font-weight:bold">list</span> <span style="color:#008000;font-weight:bold">&amp;key</span> <span style="color:#19177c">strict?</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return item if item is inside the list, nil otherwise. If strict? is nil and item is a string, the list can contain a string that just contains the item.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">or</span> <span style="color:#19177c">strict?</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#00f">not</span> (<span style="color:#00f">stringp</span> <span style="color:#19177c">item</span>)))
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">find</span> <span style="color:#19177c">item</span> <span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">:test</span> <span style="color:#19177c">&#39;equal</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">hay</span> <span style="color:#19177c">in</span> <span style="color:#00f;font-weight:bold">list</span>
</span></span><span style="display:flex;"><span>             <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">stringp</span> <span style="color:#19177c">hay</span>)
</span></span><span style="display:flex;"><span>             <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">search</span> <span style="color:#19177c">item</span> <span style="color:#19177c">hay</span>) <span style="color:#19177c">collect</span> <span style="color:#19177c">it</span>
</span></span><span style="display:flex;"><span>             <span style="color:#19177c">else</span> <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">item</span> <span style="color:#19177c">hay</span>) <span style="color:#19177c">collect</span> <span style="color:#19177c">it</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#19177c">item</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">find-all</span> (<span style="color:#19177c">symbols</span> <span style="color:#00f;font-weight:bold">list</span> <span style="color:#008000;font-weight:bold">&amp;key</span> <span style="color:#19177c">strict?</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a list of symbols and do a find for each in the list. If strict? is nil then also grep strings for a match..&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">not</span> (<span style="color:#00f">consp</span> <span style="color:#19177c">symbols</span>))
</span></span><span style="display:flex;"><span>      (<span style="color:#19177c">list-if</span> (<span style="color:#19177c">find-item</span> <span style="color:#19177c">symbols</span> <span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">:strict?</span> <span style="color:#19177c">strict?</span>))
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">symbols</span>
</span></span><span style="display:flex;"><span>                 (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#00f;font-weight:bold">symbol</span> <span style="color:#19177c">in</span> <span style="color:#19177c">symbols</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#19177c">collect</span> (<span style="color:#19177c">find-item</span> <span style="color:#00f;font-weight:bold">symbol</span> <span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">:strict?</span> <span style="color:#19177c">strict?</span>)))
</span></span><span style="display:flex;"><span>          <span style="color:#19177c">symbols</span>
</span></span><span style="display:flex;"><span>          <span style="color:#800">nil</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">find-tag-helper</span> (<span style="color:#19177c">tag</span> <span style="color:#19177c">tree</span> <span style="color:#19177c">elements</span> <span style="color:#19177c">strict?</span> <span style="color:#19177c">branches-above</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Does the work for find-tag, horrible horrible mess! *Hangs head in shame*&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#00f">eql</span> (<span style="color:#00f">car</span> <span style="color:#19177c">tree</span>) <span style="color:#19177c">tag</span>)
</span></span><span style="display:flex;"><span>           (<span style="color:#008000">or</span> (<span style="color:#00f">not</span> <span style="color:#19177c">elements</span>)
</span></span><span style="display:flex;"><span>               (<span style="color:#19177c">find-all</span> <span style="color:#19177c">elements</span> <span style="color:#19177c">tree</span> <span style="color:#19177c">:strict?</span> <span style="color:#19177c">strict?</span>)))
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">progn</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#008000">setf</span> <span style="color:#19177c">*branches-above*</span> <span style="color:#19177c">branches-above</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">tree</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">goal</span> (<span style="color:#008000">or</span> (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">consp</span> (<span style="color:#00f">car</span> <span style="color:#19177c">tree</span>))
</span></span><span style="display:flex;"><span>                          (<span style="color:#19177c">find-tag-helper</span> <span style="color:#19177c">tag</span> (<span style="color:#00f">car</span> <span style="color:#19177c">tree</span>) <span style="color:#19177c">elements</span> <span style="color:#19177c">strict?</span> <span style="color:#19177c">branches-above</span>))
</span></span><span style="display:flex;"><span>                      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">consp</span> (<span style="color:#00f">cdr</span> <span style="color:#19177c">tree</span>))
</span></span><span style="display:flex;"><span>                          (<span style="color:#19177c">find-tag-helper</span> <span style="color:#19177c">tag</span> (<span style="color:#00f">cdr</span> <span style="color:#19177c">tree</span>) <span style="color:#19177c">elements</span> <span style="color:#19177c">strict?</span> <span style="color:#19177c">branches-above</span>)))))
</span></span><span style="display:flex;"><span>        (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#00f">numberp</span> <span style="color:#19177c">*branches-above*</span>)
</span></span><span style="display:flex;"><span>                 (<span style="color:#00f">&gt;</span> <span style="color:#19177c">*branches-above*</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#008000;font-weight:bold">progn</span>
</span></span><span style="display:flex;"><span>              (<span style="color:#008000">decf</span> <span style="color:#19177c">*branches-above*</span>)
</span></span><span style="display:flex;"><span>              <span style="color:#19177c">tree</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#19177c">goal</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">find-tag</span> (<span style="color:#19177c">tag</span> <span style="color:#19177c">tree</span> <span style="color:#008000;font-weight:bold">&amp;key</span> <span style="color:#19177c">elements</span> (<span style="color:#19177c">strict?</span> <span style="color:#800">t</span>) (<span style="color:#19177c">branches-above</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a tree, a tag and the tag&#39;s value and return the rest of the tree from that point.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">*branches-above*</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">find-tag-helper</span> <span style="color:#19177c">tag</span> <span style="color:#19177c">tree</span> <span style="color:#19177c">elements</span> <span style="color:#19177c">strict?</span> <span style="color:#19177c">branches-above</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defmacro</span> <span style="color:#19177c">add-page</span> (<span style="color:#19177c">page-list</span> <span style="color:#19177c">url</span> <span style="color:#19177c">name</span> <span style="color:#008000;font-weight:bold">&amp;body</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#666">`</span>(<span style="color:#008000">push</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#666">,</span><span style="color:#19177c">url</span> <span style="color:#666">,</span><span style="color:#19177c">name</span>
</span></span><span style="display:flex;"><span>               (<span style="color:#008000">lambda</span> (<span style="color:#19177c">html</span>) <span style="color:#666">,@</span><span style="color:#19177c">body</span>)) <span style="color:#666">,</span><span style="color:#19177c">page-list</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">check-page</span> (<span style="color:#19177c">URL</span> <span style="color:#19177c">name</span> <span style="color:#19177c">func</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a URL, download the page and have a look for differences&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let*</span> ((<span style="color:#19177c">filename</span> (<span style="color:#00f">concatenate</span> <span style="color:#19177c">&#39;string</span> <span style="color:#19177c">*store*</span> <span style="color:#19177c">name</span> <span style="color:#ba2121">&#34;/old&#34;</span>))
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">new-file</span> (<span style="color:#00f">funcall</span> <span style="color:#19177c">func</span> (<span style="color:#19177c">parse-html</span> (<span style="color:#19177c">download</span> <span style="color:#19177c">URL</span>)))))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">ensure-directories-exist</span> <span style="color:#19177c">filename</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#19177c">file-exists?</span> <span style="color:#19177c">filename</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">old-file</span> (<span style="color:#00f">car</span> (<span style="color:#19177c">load-html</span> <span style="color:#19177c">filename</span>)))) <span style="color:#408080;font-style:italic">;; FIXME why is this (car needed?!</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">old-file</span> <span style="color:#19177c">new-file</span>)
</span></span><span style="display:flex;"><span>              (<span style="color:#19177c">same-page</span> <span style="color:#19177c">name</span> <span style="color:#19177c">new-file</span>)
</span></span><span style="display:flex;"><span>              (<span style="color:#19177c">changed-page</span> <span style="color:#19177c">name</span>
</span></span><span style="display:flex;"><span>                            (<span style="color:#19177c">save-html</span> <span style="color:#19177c">filename</span> <span style="color:#19177c">new-file</span>)
</span></span><span style="display:flex;"><span>                            (<span style="color:#19177c">html-diff:HTML-DIFF</span> (<span style="color:#19177c">craft-html</span> <span style="color:#19177c">old-file</span>) (<span style="color:#19177c">craft-html</span> <span style="color:#19177c">new-file</span>)))))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">new-page</span> <span style="color:#19177c">name</span> (<span style="color:#19177c">save-html</span> <span style="color:#19177c">filename</span> <span style="color:#19177c">new-file</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">new-page</span> (<span style="color:#19177c">title</span> <span style="color:#19177c">html</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Function that&#39;s called when a page is checked for the first time&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">log-message</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;NEW Page - ~A~%&#34;</span> <span style="color:#19177c">title</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">spam</span> <span style="color:#ba2121">&#34;kzar@kzar.co.uk&#34;</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;**Visa Alert** - New Page (~A)&#34;</span> <span style="color:#19177c">title</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;I have started watching this new page called \&#34;~A\&#34;. ~%~%Here&#39;s the HTML:~%~%~A&#34;</span>  <span style="color:#19177c">title</span> <span style="color:#19177c">html</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">same-page</span> (<span style="color:#19177c">title</span> <span style="color:#19177c">html</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Called when the page is the same, (possibly does nothing!)&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">log-message</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;no change to - ~A~%&#34;</span> <span style="color:#19177c">title</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">changed-page</span> (<span style="color:#19177c">title</span> <span style="color:#19177c">html</span> <span style="color:#19177c">diff</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Function that&#39;s called when a page changed. Email everyone and sound the sirens!&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">log-message</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;CHANGED PAGE - ~A~%&#34;</span> <span style="color:#19177c">title</span>))
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">spam</span> <span style="color:#ba2121">&#34;kzar@kzar.co.uk&#34;</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;**Visa Alert** - Changed Page (~A)&#34;</span> <span style="color:#19177c">title</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;The page ~A has changed! Below is a diff: ~%~%~A&#34;</span> <span style="color:#19177c">title</span> <span style="color:#19177c">diff</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">check-pages</span> (<span style="color:#19177c">pages</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Check all the pages given by for changes.&#34;</span>
</span></span><span style="display:flex;"><span>   (<span style="color:#008000">dolist</span> (<span style="color:#19177c">page</span> <span style="color:#19177c">pages</span>)
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">check-page</span> (<span style="color:#00f">car</span> <span style="color:#19177c">page</span>) (<span style="color:#00f">cadr</span> <span style="color:#19177c">page</span>) (<span style="color:#00f">caddr</span> <span style="color:#19177c">page</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000;font-weight:bold">let</span> (<span style="color:#19177c">pages</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">add-page</span> <span style="color:#19177c">pages</span> <span style="color:#ba2121">&#34;http://www.bunac.org/UK/workcanada/applying.aspx&#34;</span> <span style="color:#ba2121">&#34;Visa application Page&#34;</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">find-tag</span> <span style="color:#19177c">:style</span> <span style="color:#19177c">html</span> <span style="color:#19177c">:elements</span> <span style="color:#666">&#39;</span>(<span style="color:#ba2121">&#34;#FF924A&#34;</span>) <span style="color:#19177c">:strict?</span> <span style="color:#800">nil</span> <span style="color:#19177c">:branches-above</span> <span style="color:#666">2</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">add-page</span> <span style="color:#19177c">pages</span> <span style="color:#ba2121">&#34;http://kzar.co.uk/testpage&#34;</span> <span style="color:#ba2121">&#34;Test Page&#34;</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">car</span> <span style="color:#19177c">html</span>)) <span style="color:#408080;font-style:italic">; FIXME WHY IS THIS CAR REQUIRED?!</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">add-page</span> <span style="color:#19177c">pages</span> <span style="color:#ba2121">&#34;http://www.bunac.org/uk/workcanada/&#34;</span> <span style="color:#ba2121">&#34;Visa Homepage&#34;</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">find-tag</span> <span style="color:#19177c">:div</span> <span style="color:#19177c">html</span> <span style="color:#19177c">:elements</span> <span style="color:#666">&#39;</span>(<span style="color:#ba2121">&#34;width:480px&#34;</span>) <span style="color:#19177c">:strict?</span> <span style="color:#800">nil</span> <span style="color:#19177c">:branches-above</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">add-page</span> <span style="color:#19177c">pages</span> <span style="color:#ba2121">&#34;https://www.bunac.org/applyCanada/&#34;</span> <span style="color:#ba2121">&#34;Visa payment page&#34;</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">find-tag</span> <span style="color:#19177c">:div</span> <span style="color:#19177c">html</span> <span style="color:#19177c">:elements</span> <span style="color:#ba2121">&#34;bodyContent&#34;</span> <span style="color:#19177c">:strict?</span> <span style="color:#800">nil</span> <span style="color:#19177c">:branches-above</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">check-pages</span> <span style="color:#19177c">pages</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">quit</span>)</span></span></code></pre></div>
<p><em>(Please bear in mind that I threw this code together very quickly. If you are using it for something important make sure it works correctly first!)</em></p>
<p>I had it running every hour by putting a shell script in my /etc/cron.hourly to run the code in a new SBCL:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#bc7a00">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#bc7a00"></span>/usr/src/clbuild/clbuild preloaded --load /var/lisp/visa-check.cl</span></span></code></pre></div>
<p>(That&rsquo;s why there&rsquo;s a <code>(quit)</code> at the end of the Lisp code. With a new SBCL instance firing up once an hour we need to make sure the old ones are closed!)</p>
<p>It seems to work pretty nicely anyway. It emails you notification of changes like this:</p>
<p><img src="/images/blog/visa-change.webp" alt="Change notification"></p>
<p>Cheers, Dave</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/12/12/simple-genetic-algorithm-lisp</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/12/12/simple-genetic-algorithm-lisp" />
    <title>Simple genetic algorithm in Lisp</title>
    <published>2008-12-12T00:00:00+00:00</published>
    <updated>2008-12-12T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>After being inspired by this <a href="http://www.wreck.devisland.net/ga/">fantastic genetic algorithm demo</a>, I decided to get learning.</p>
<p>For my first attempt at anything like this, I found an <a href="http://www.ai-junkie.com/ga/intro/gat1.html"><del>excellent guide</del></a> [1] and programmed the example. The algorithm had the task of making an equation that equalled a goal number. So, for example, I might tell it &ldquo;Make me an equation nine long that creates 13&rdquo;, and it could come back with <code>(1 * 9 + 2 + 6 - 4)</code>.</p>
<p>It took quite a while to get it working since the algorithm isn&rsquo;t deterministic I found the code quite hard to debug. It all works well now though, and I&rsquo;m quite pleased with the code, it&rsquo;s clean, and I managed to optimise it to run a lot faster.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*genes*</span> <span style="color:#666">&#39;</span>((<span style="color:#666">0</span> (<span style="color:#666">0</span> <span style="color:#666">0</span> <span style="color:#666">0</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">1</span> (<span style="color:#666">0</span> <span style="color:#666">0</span> <span style="color:#666">0</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">2</span> (<span style="color:#666">0</span> <span style="color:#666">0</span> <span style="color:#666">1</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">3</span> (<span style="color:#666">0</span> <span style="color:#666">0</span> <span style="color:#666">1</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">4</span> (<span style="color:#666">0</span> <span style="color:#666">1</span> <span style="color:#666">0</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">5</span> (<span style="color:#666">0</span> <span style="color:#666">1</span> <span style="color:#666">0</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">6</span> (<span style="color:#666">0</span> <span style="color:#666">1</span> <span style="color:#666">1</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">7</span> (<span style="color:#666">0</span> <span style="color:#666">1</span> <span style="color:#666">1</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">8</span> (<span style="color:#666">1</span> <span style="color:#666">0</span> <span style="color:#666">0</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">9</span> (<span style="color:#666">1</span> <span style="color:#666">0</span> <span style="color:#666">0</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#00f">+</span> (<span style="color:#666">1</span> <span style="color:#666">0</span> <span style="color:#666">1</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#00f">-</span> (<span style="color:#666">1</span> <span style="color:#666">0</span> <span style="color:#666">1</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#00f">*</span> (<span style="color:#666">1</span> <span style="color:#666">1</span> <span style="color:#666">1</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#00f">/</span> (<span style="color:#666">1</span> <span style="color:#666">1</span> <span style="color:#666">0</span> <span style="color:#666">1</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*mutation-rate*</span> <span style="color:#666">0.001</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*crossover-rate*</span> <span style="color:#666">0.7</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">random-item</span> (<span style="color:#00f;font-weight:bold">list</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a list and return one item from it at random&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">nth</span> (<span style="color:#00f">random</span> (<span style="color:#00f">length</span> <span style="color:#00f;font-weight:bold">list</span>)) <span style="color:#00f;font-weight:bold">list</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">generate-random-chromosome</span> (<span style="color:#19177c">size</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">i</span> <span style="color:#19177c">from</span> <span style="color:#666">1</span> <span style="color:#19177c">to</span> <span style="color:#19177c">size</span>
</span></span><span style="display:flex;"><span>     <span style="color:#00f">append</span> (<span style="color:#00f">cadr</span> (<span style="color:#19177c">random-item</span> <span style="color:#19177c">*genes*</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">decode-gene</span> (<span style="color:#19177c">gene</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">car</span> (<span style="color:#00f">rassoc</span> <span style="color:#19177c">gene</span> <span style="color:#19177c">*genes*</span> <span style="color:#19177c">:key</span> <span style="color:#00f">#&#39;car</span> <span style="color:#19177c">:test</span> <span style="color:#00f">#&#39;equal</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">decode-chromosome</span> (<span style="color:#19177c">chromosome</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">remove</span> <span style="color:#800">nil</span> (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> (<span style="color:#19177c">a</span> <span style="color:#19177c">b</span> <span style="color:#19177c">c</span> <span style="color:#19177c">d</span>) <span style="color:#19177c">on</span> <span style="color:#19177c">chromosome</span> <span style="color:#19177c">by</span> <span style="color:#00f">#&#39;cddddr</span>
</span></span><span style="display:flex;"><span>                <span style="color:#19177c">collect</span> (<span style="color:#19177c">decode-gene</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">a</span> <span style="color:#19177c">b</span> <span style="color:#19177c">c</span> <span style="color:#19177c">d</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; FIXME x / 0 is silently dropped and operator precedence isn&#39;t correct</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">find-answer</span> (<span style="color:#19177c">dc</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Find the answer given by a decoded-chromosome&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">cond</span> ((<span style="color:#00f">not</span> (<span style="color:#00f">consp</span> <span style="color:#19177c">dc</span>)) <span style="color:#19177c">dc</span>)
</span></span><span style="display:flex;"><span>        ((<span style="color:#00f">&lt;</span> (<span style="color:#00f">list-length</span> <span style="color:#19177c">dc</span>) <span style="color:#666">3</span>) (<span style="color:#00f">car</span> (<span style="color:#00f">remove-if-not</span> <span style="color:#19177c">&#39;numberp</span> <span style="color:#19177c">dc</span>)))
</span></span><span style="display:flex;"><span>        ((<span style="color:#00f">not</span> (<span style="color:#00f">numberp</span> (<span style="color:#00f">car</span> <span style="color:#19177c">dc</span>))) (<span style="color:#19177c">find-answer</span> (<span style="color:#00f">cdr</span> <span style="color:#19177c">dc</span>)))
</span></span><span style="display:flex;"><span>        ((<span style="color:#00f">numberp</span> (<span style="color:#00f">cadr</span> <span style="color:#19177c">dc</span>)) (<span style="color:#19177c">find-answer</span> (<span style="color:#00f;font-weight:bold">cons</span> (<span style="color:#00f">car</span> <span style="color:#19177c">dc</span>) (<span style="color:#00f">cddr</span> <span style="color:#19177c">dc</span>))))
</span></span><span style="display:flex;"><span>        ((<span style="color:#00f">not</span> (<span style="color:#00f">numberp</span> (<span style="color:#00f">caddr</span> <span style="color:#19177c">dc</span>)))
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">find-answer</span> (<span style="color:#00f">append</span> (<span style="color:#00f">subseq</span> <span style="color:#19177c">dc</span> <span style="color:#666">0</span> <span style="color:#666">2</span>) (<span style="color:#00f">cdddr</span> <span style="color:#19177c">dc</span>))))
</span></span><span style="display:flex;"><span>        ((<span style="color:#008000">and</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">&#39;/</span> (<span style="color:#00f">cadr</span> <span style="color:#19177c">dc</span>))
</span></span><span style="display:flex;"><span>              (<span style="color:#00f">eql</span> <span style="color:#666">0</span> (<span style="color:#00f">caddr</span> <span style="color:#19177c">dc</span>)))
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">find-answer</span> (<span style="color:#00f;font-weight:bold">cons</span> (<span style="color:#00f">car</span> <span style="color:#19177c">dc</span>) (<span style="color:#00f">cddr</span> <span style="color:#19177c">dc</span>))))
</span></span><span style="display:flex;"><span>        (<span style="color:#800">t</span>
</span></span><span style="display:flex;"><span>         (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">simplified</span> (<span style="color:#00f">eval</span> (<span style="color:#00f;font-weight:bold">list</span> (<span style="color:#00f">cadr</span> <span style="color:#19177c">dc</span>) (<span style="color:#00f">car</span> <span style="color:#19177c">dc</span>) (<span style="color:#00f">caddr</span> <span style="color:#19177c">dc</span>)))))
</span></span><span style="display:flex;"><span>           (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">consp</span> (<span style="color:#00f">cdddr</span> <span style="color:#19177c">dc</span>))
</span></span><span style="display:flex;"><span>               (<span style="color:#19177c">find-answer</span> (<span style="color:#00f;font-weight:bold">cons</span> <span style="color:#19177c">simplified</span> (<span style="color:#00f">cdddr</span> <span style="color:#19177c">dc</span>)))
</span></span><span style="display:flex;"><span>               <span style="color:#19177c">simplified</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">count-flaws</span> (<span style="color:#19177c">dc</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return the number of items in a chromosome which are semantically wrong e.g. &#39;(* / +) would be 3&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">should-be-number</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">flaw-count</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">gene</span> <span style="color:#19177c">in</span> <span style="color:#19177c">dc</span>
</span></span><span style="display:flex;"><span>       <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">not</span> (<span style="color:#00f">eql</span> (<span style="color:#00f">numberp</span> <span style="color:#19177c">gene</span>) <span style="color:#19177c">should-be-number</span>))
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">incf</span> <span style="color:#19177c">flaw-count</span>)
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">setf</span> <span style="color:#19177c">should-be-number</span> (<span style="color:#008000;font-weight:bold">if</span> <span style="color:#19177c">should-be-number</span> <span style="color:#800">nil</span> <span style="color:#800">t</span>)))
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">flaw-count</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; FIXME -100 is given to all symbol chromosomes, probably a bad idea?</span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">fitness</span> (<span style="color:#19177c">chromosome</span> <span style="color:#19177c">goal</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return a fitness based on the distance from the answer and the number of flaws&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">answer</span> (<span style="color:#19177c">find-answer</span> (<span style="color:#19177c">decode-chromosome</span> <span style="color:#19177c">chromosome</span>))))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">numberp</span> <span style="color:#19177c">answer</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">distance</span> (<span style="color:#00f">abs</span> (<span style="color:#00f">-</span> <span style="color:#19177c">goal</span> <span style="color:#19177c">answer</span>)))
</span></span><span style="display:flex;"><span>              (<span style="color:#19177c">flaws</span> (<span style="color:#19177c">count-flaws</span> (<span style="color:#19177c">decode-chromosome</span> <span style="color:#19177c">chromosome</span>))))
</span></span><span style="display:flex;"><span>          (<span style="color:#00f">+</span> (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">distance</span> <span style="color:#666">0</span>) <span style="color:#666">0</span> (<span style="color:#00f">/</span> <span style="color:#666">1</span> <span style="color:#19177c">distance</span>))
</span></span><span style="display:flex;"><span>             (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">flaws</span> <span style="color:#666">0</span>) <span style="color:#666">0</span> (<span style="color:#00f">/</span> <span style="color:#666">1</span> <span style="color:#19177c">flaws</span>))))
</span></span><span style="display:flex;"><span>        <span style="color:#666">-100</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">pool-fitness</span> (<span style="color:#19177c">pool</span> <span style="color:#19177c">goal</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">chromosome</span> <span style="color:#19177c">in</span> <span style="color:#19177c">pool</span>
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">collect</span> (<span style="color:#19177c">fitness</span> <span style="color:#19177c">chromosome</span> <span style="color:#19177c">goal</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">mutate-bit</span> (<span style="color:#b00040">bit</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a 1 or 0 and mutate it.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">&lt;</span> (<span style="color:#00f">random</span> <span style="color:#666">1.0</span>) <span style="color:#19177c">*mutation-rate*</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">eql</span> <span style="color:#b00040">bit</span> <span style="color:#666">0</span>) <span style="color:#666">1</span> <span style="color:#666">0</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#b00040">bit</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">mutate</span> (<span style="color:#19177c">chromosome</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Returns a possibly mutated version of chromosome&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#b00040">bit</span> <span style="color:#19177c">in</span> <span style="color:#19177c">chromosome</span>
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">collect</span> (<span style="color:#19177c">mutate-bit</span> <span style="color:#b00040">bit</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">crossover</span> (<span style="color:#00f">first</span> <span style="color:#00f">second</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Returns a mix of two chromosomes (might be the same)&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">&lt;</span> (<span style="color:#00f">random</span> <span style="color:#666">1.0</span>) <span style="color:#19177c">*crossover-rate*</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">point</span> (<span style="color:#00f">+</span> (<span style="color:#00f">random</span> (<span style="color:#00f">-</span> (<span style="color:#00f">length</span> <span style="color:#00f">first</span>) <span style="color:#666">1</span>)) <span style="color:#666">1</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">append</span> (<span style="color:#00f">subseq</span> <span style="color:#00f">first</span> <span style="color:#666">0</span> <span style="color:#19177c">point</span>)
</span></span><span style="display:flex;"><span>                (<span style="color:#00f">subseq</span> <span style="color:#00f">second</span> <span style="color:#19177c">point</span>)))
</span></span><span style="display:flex;"><span>      (<span style="color:#19177c">random-item</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#00f">first</span> <span style="color:#00f">second</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">make-roulette-wheel</span> (<span style="color:#19177c">fitness</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let*</span> ((<span style="color:#19177c">total-fitness</span> (<span style="color:#00f">reduce</span> <span style="color:#00f">#&#39;+</span> <span style="color:#19177c">fitness</span>))
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">total-probability</span> <span style="color:#666">0.0</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">append</span> (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">x</span> <span style="color:#19177c">in</span> <span style="color:#19177c">fitness</span>
</span></span><span style="display:flex;"><span>               <span style="color:#19177c">collect</span> <span style="color:#19177c">total-probability</span>
</span></span><span style="display:flex;"><span>               <span style="color:#008000">do</span> (<span style="color:#008000">incf</span> <span style="color:#19177c">total-probability</span> (<span style="color:#00f">/</span> <span style="color:#19177c">x</span> <span style="color:#19177c">total-fitness</span>))) <span style="color:#666">&#39;</span>(<span style="color:#666">1.0</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">spin-the-wheel</span> (<span style="color:#19177c">pool</span> <span style="color:#19177c">roulette-wheel</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">ball</span> (<span style="color:#00f">random</span> <span style="color:#666">1.0</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">declare</span> (<span style="color:#008000;font-weight:bold">type</span> <span style="color:#00f;font-weight:bold">float</span> <span style="color:#19177c">ball</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">chromosome</span> <span style="color:#19177c">in</span> <span style="color:#19177c">pool</span>
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">for</span> (<span style="color:#00f">position</span> <span style="color:#19177c">next-pos</span>) <span style="color:#19177c">of-type</span> (<span style="color:#00f;font-weight:bold">float</span> <span style="color:#00f;font-weight:bold">float</span>) <span style="color:#19177c">on</span> <span style="color:#19177c">roulette-wheel</span>
</span></span><span style="display:flex;"><span>       <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">&lt;=</span> <span style="color:#19177c">ball</span> <span style="color:#19177c">next-pos</span>)
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">return</span> <span style="color:#19177c">chromosome</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">re-populate</span> (<span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">roulette-wheel</span> (<span style="color:#19177c">make-roulette-wheel</span> <span style="color:#19177c">fitness</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">i</span> <span style="color:#19177c">from</span> <span style="color:#666">1</span> <span style="color:#19177c">to</span> (<span style="color:#00f">length</span> <span style="color:#19177c">pool</span>)
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">collect</span> (<span style="color:#19177c">mutate</span> (<span style="color:#19177c">crossover</span> (<span style="color:#19177c">spin-the-wheel</span> <span style="color:#19177c">pool</span> <span style="color:#19177c">roulette-wheel</span>)
</span></span><span style="display:flex;"><span>                                  (<span style="color:#19177c">spin-the-wheel</span> <span style="color:#19177c">pool</span> <span style="color:#19177c">roulette-wheel</span>))))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">create-initial-pool</span> (<span style="color:#19177c">pool-size</span> <span style="color:#19177c">chromosome-size</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">i</span> <span style="color:#19177c">from</span> <span style="color:#666">1</span> <span style="color:#19177c">to</span> <span style="color:#19177c">pool-size</span>
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">collect</span> (<span style="color:#19177c">generate-random-chromosome</span> <span style="color:#19177c">chromosome-size</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">find-best-chromosome</span> (<span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Returns the fittest chromosome in the pool.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">best-score</span>) (<span style="color:#19177c">best-chromosome</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">chromosome</span> <span style="color:#19177c">in</span> <span style="color:#19177c">pool</span>
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">for</span> <span style="color:#19177c">score</span> <span style="color:#19177c">in</span> <span style="color:#19177c">fitness</span>
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">when</span> (<span style="color:#008000">or</span> (<span style="color:#00f">equalp</span> <span style="color:#19177c">score</span> <span style="color:#666">0</span>) (<span style="color:#00f">not</span> <span style="color:#19177c">best-score</span>) (<span style="color:#00f">&gt;</span> <span style="color:#19177c">score</span> <span style="color:#19177c">best-score</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#008000">setf</span> <span style="color:#19177c">best-score</span> <span style="color:#19177c">score</span>)
</span></span><span style="display:flex;"><span>            (<span style="color:#008000">setf</span> <span style="color:#19177c">best-chromosome</span> <span style="color:#19177c">chromosome</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">values</span> <span style="color:#19177c">best-chromosome</span> <span style="color:#19177c">best-score</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">there-is-a-winner</span> (<span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;If any of the chromosomes in the pool have the answer return the first one that does.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">winner</span> (<span style="color:#00f">position</span> <span style="color:#666">0</span> <span style="color:#19177c">fitness</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> <span style="color:#19177c">winner</span> (<span style="color:#00f">nth</span> <span style="color:#19177c">winner</span> <span style="color:#19177c">pool</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">display-turn</span> (<span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span> <span style="color:#19177c">turn</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">multiple-value-bind</span> (<span style="color:#19177c">chromosome</span> <span style="color:#19177c">score</span>) (<span style="color:#19177c">find-best-chromosome</span> <span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">avg-fitness</span> (<span style="color:#00f">/</span> (<span style="color:#00f">reduce</span> <span style="color:#00f">#&#39;+</span> <span style="color:#19177c">fitness</span>) (<span style="color:#00f">+</span> <span style="color:#666">1</span> (<span style="color:#00f">length</span> <span style="color:#19177c">pool</span>))))
</span></span><span style="display:flex;"><span>          (<span style="color:#19177c">*print-pretty*</span> <span style="color:#800">nil</span>))
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">format</span> <span style="color:#800">t</span> <span style="color:#ba2121">&#34;~a - Average Fitness: ~F Best: ~w (fitness ~F)~%&#34;</span> <span style="color:#19177c">turn</span> <span style="color:#19177c">avg-fitness</span> (<span style="color:#19177c">decode-chromosome</span> <span style="color:#19177c">chromosome</span>) <span style="color:#19177c">score</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">genetic-algorithm</span> (<span style="color:#19177c">goal</span> <span style="color:#19177c">pop-size</span> <span style="color:#19177c">chromosome-size</span> <span style="color:#19177c">tries</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">pool</span> (<span style="color:#19177c">create-initial-pool</span> <span style="color:#19177c">pop-size</span> <span style="color:#19177c">chromosome-size</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">fitness</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">i</span> <span style="color:#19177c">from</span> <span style="color:#666">1</span> <span style="color:#19177c">to</span> <span style="color:#19177c">tries</span>
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">setf</span> <span style="color:#19177c">fitness</span> (<span style="color:#19177c">pool-fitness</span> <span style="color:#19177c">pool</span> <span style="color:#19177c">goal</span>))
</span></span><span style="display:flex;"><span>       <span style="color:#008000;font-weight:bold">if</span> (<span style="color:#19177c">there-is-a-winner</span> <span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span>) <span style="color:#008000">return</span> <span style="color:#19177c">it</span>
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#19177c">display-turn</span> <span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span> <span style="color:#19177c">i</span>)
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">setf</span> <span style="color:#19177c">pool</span> (<span style="color:#19177c">re-populate</span> <span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span>))
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">finally</span> (<span style="color:#008000">return</span> (<span style="color:#19177c">find-best-chromosome</span> <span style="color:#19177c">pool</span> <span style="color:#19177c">fitness</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">skynet</span> (<span style="color:#008000;font-weight:bold">&amp;key</span> <span style="color:#19177c">goal</span> <span style="color:#19177c">pop-size</span> <span style="color:#19177c">chromosome-size</span> <span style="color:#19177c">tries</span> <span style="color:#19177c">mutation-rate</span> <span style="color:#19177c">crossover-rate</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">best</span> (<span style="color:#19177c">genetic-algorithm</span> <span style="color:#19177c">goal</span> <span style="color:#19177c">pop-size</span> <span style="color:#19177c">chromosome-size</span> <span style="color:#19177c">tries</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">*print-pretty*</span> <span style="color:#800">nil</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">*mutation-rate*</span> <span style="color:#19177c">mutation-rate</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">*crossover-rate*</span> <span style="color:#19177c">crossover-rate</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">format</span> <span style="color:#800">t</span> <span style="color:#ba2121">&#34;~w gave ~F (Aim was ~A)&#34;</span> (<span style="color:#19177c">decode-chromosome</span> <span style="color:#19177c">best</span>) (<span style="color:#19177c">find-answer</span> (<span style="color:#19177c">decode-chromosome</span> <span style="color:#19177c">best</span>)) <span style="color:#19177c">goal</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Sensible parameter values according to t&#39;internet:</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;   - 0.1 to 0.001 for mutation rate</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;   - 0.7 to 0.9 for crossover rate</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">skynet</span> <span style="color:#19177c">:goal</span> <span style="color:#666">13</span>
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">:pop-size</span> <span style="color:#666">100</span>
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">:chromosome-size</span> <span style="color:#666">9</span>
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">:tries</span> <span style="color:#666">200</span>
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">:mutation-rate</span> <span style="color:#666">0.001</span>
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">:crossover-rate</span> <span style="color:#666">0.7</span>)</span></span></code></pre></div>
<p>As always feel free to use the code, any feedback would be useful. Next up I&rsquo;m going to apply GA to a more interesting problem.</p>
<p>Cheers, Dave.</p>
<p>Edit: I optimised the code by using the SBCL profiler to see what was slowing things down. It turned out that the <code>spin-the-wheel</code> function was taking about 92% of the time with the functions it called. Firstly I examined the function and realised I could reduce the comparisons, that reduced it to about 87%. Next, the guys in #lisp explained that I should declare the variables in that function as floats so that SBCL could optimise things for me. When I tried that it reduced the time spent in <code>spin-the-wheel</code> down to around 36%!</p>
<p>I found it surprising that the &lsquo;roulette wheel spinning&rsquo; function that we use to select chromosomes for crossover was the most computationally intensive part of the code. I&rsquo;m curious to know if that&rsquo;s normal. Also if you have any idea how I could optimise more I would be grateful, <a href="/files/blog/genetic-algorithm-profiling-output">here is the profiler output</a>.</p>
<p>[1] - According to the comments the guide is teaching very out of date methods, possibly consider a different source of information!</p>
<hr>
<p><em>Edit: Before I replaced WordPress there were some excellent comments:</em></p>
<p><strong>Foo said</strong></p>
<blockquote>
That tutorial is really quite astonishingly crappy. So what I'm going to say is obviously exaggeration but is close to the truth:
<p>(1) NO ONE has done roulette selection in the last ten years. Tournament selection, truncation selection, etc., yes. Roulette selection? Laughable.</p>
<p>(2) NO ONE has done binary encodings of non-binary problems in the last ten years. You use an encoding which matches your actual problem.</p>
<p>(3) NO ONE does one-point crossover any more. A parametrisation uniform crossover. Or at LEAST, heaven forbid, a two-point crossover.</p>
<p>(4) NO ONE does genetic programming (the little encoding of math the guy&rsquo;s showing) in a fixed-length string. It borders on the hilariously ignorant.</p>
<p>Hey, it&rsquo;s all fine if you&rsquo;re doing this for the first time, and you go pick up a 1970 book on genetic algorithms and publish a web page on how you coded it up. But to claim that you&rsquo;re doing a TUTORIAL? This guy should have been taken out behind the shed and shot.</p>
</blockquote>
<p><strong>Dominic Said</strong></p>
<blockquote>
You might find this tutorial useful: http://www.obitko.com/tutorials/genetic-algorithms/index.php
<p>Also, the &ldquo;field guide to genetic programming&rdquo; by Poli, Langdon and McPhee is very up-to-date, and can be downloaded for free: <a href="http://www.lulu.com/content/2167025">http://www.lulu.com/content/2167025</a></p>
</blockquote>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/11/26/month-learning-game</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/11/26/month-learning-game" />
    <title>Month learning game</title>
    <published>2008-11-26T00:00:00+00:00</published>
    <updated>2008-11-26T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>After the horrible mess that was the scraper script, I decided to make something a bit cleaner.</p>
<p>It&rsquo;s a nice little game that teaches you about the different months.</p>
<p>It works pretty well, but I think the code needs some work:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#008000">defpackage</span> <span style="color:#19177c">:month-game</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">:use</span> <span style="color:#19177c">:cl</span> <span style="color:#19177c">:hunchentoot</span> <span style="color:#19177c">:cl-who</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">in-package</span> <span style="color:#19177c">:month-game</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">push</span> (<span style="color:#19177c">create-regex-dispatcher</span> <span style="color:#ba2121">&#34;^/lisp/months$&#34;</span> <span style="color:#19177c">&#39;months-page</span>) <span style="color:#19177c">*dispatch-table*</span>)
</span></span><span style="display:flex;"><span>(<span style="color:#008000">push</span> (<span style="color:#19177c">create-regex-dispatcher</span> <span style="color:#ba2121">&#34;^/months$&#34;</span> <span style="color:#19177c">&#39;months-page</span>) <span style="color:#19177c">*dispatch-table*</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*months*</span> <span style="color:#666">&#39;</span>((<span style="color:#666">1</span> <span style="color:#ba2121">&#34;January&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">2</span> <span style="color:#ba2121">&#34;February&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">3</span> <span style="color:#ba2121">&#34;March&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">4</span> <span style="color:#ba2121">&#34;April&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">5</span> <span style="color:#ba2121">&#34;May&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">6</span> <span style="color:#ba2121">&#34;June&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">7</span> <span style="color:#ba2121">&#34;July&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">8</span> <span style="color:#ba2121">&#34;August&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">9</span> <span style="color:#ba2121">&#34;September&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">10</span> <span style="color:#ba2121">&#34;October&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">11</span> <span style="color:#ba2121">&#34;November&#34;</span>)
</span></span><span style="display:flex;"><span>                   (<span style="color:#666">12</span> <span style="color:#ba2121">&#34;December&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">month-name</span> (<span style="color:#19177c">month</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a month and return it&#39;s name&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">cadr</span> <span style="color:#19177c">month</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">month-number</span> (<span style="color:#19177c">month</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a month and return it&#39;s number&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">car</span> <span style="color:#19177c">month</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">random-item</span> (<span style="color:#00f;font-weight:bold">list</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a list and return one item from it at random&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">nth</span> (<span style="color:#00f">random</span> (<span style="color:#00f">length</span> <span style="color:#00f;font-weight:bold">list</span>)) <span style="color:#00f;font-weight:bold">list</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defmacro</span> <span style="color:#19177c">create-question-template</span> (<span style="color:#008000;font-weight:bold">&amp;body</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Macro to make adding question templates easier&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">`</span>(<span style="color:#008000">lambda</span> () <span style="color:#666">,@</span><span style="color:#19177c">body</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*question-templates*</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f;font-weight:bold">list</span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">create-question-template</span>
</span></span><span style="display:flex;"><span>     <span style="color:#ba2121">&#34;Take a month, ask them which number it is.&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#008000;font-weight:bold">let*</span> ((<span style="color:#19177c">month</span> (<span style="color:#00f">assoc</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> (<span style="color:#00f">random</span> <span style="color:#666">12</span>)) <span style="color:#19177c">*months*</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">question</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;What is the ~:r month called?&#34;</span> (<span style="color:#19177c">month-number</span> <span style="color:#19177c">month</span>)))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">correct-response</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;That&#39;s correct!~%&#34;</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">wrong-response</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;Wrong, the ~:r month is ~A~%&#34;</span> (<span style="color:#19177c">month-number</span> <span style="color:#19177c">month</span>) (<span style="color:#19177c">month-name</span> <span style="color:#19177c">month</span>)))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">test-fun</span> (<span style="color:#008000">lambda</span> (<span style="color:#19177c">answer</span>)
</span></span><span style="display:flex;"><span>                        (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">string-equal</span> <span style="color:#19177c">answer</span>
</span></span><span style="display:flex;"><span>                                          (<span style="color:#19177c">month-name</span> <span style="color:#19177c">month</span>))
</span></span><span style="display:flex;"><span>                            <span style="color:#800">t</span>))))
</span></span><span style="display:flex;"><span>       (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">question</span> <span style="color:#19177c">test-fun</span> <span style="color:#19177c">correct-response</span> <span style="color:#19177c">wrong-response</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">create-question-template</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ba2121">&#34;Give a month and take it&#39;s number.&#34;</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">let*</span> ((<span style="color:#19177c">month</span> (<span style="color:#00f">assoc</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> (<span style="color:#00f">random</span> <span style="color:#666">12</span>)) <span style="color:#19177c">*months*</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">question</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;Which number month is ~A?&#34;</span> (<span style="color:#19177c">month-name</span> <span style="color:#19177c">month</span>)))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">correct-response</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;That&#39;s correct!~%&#34;</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">wrong-response</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;No, that&#39;s wrong.. ~A is the ~:r month.~%&#34;</span> (<span style="color:#19177c">month-name</span> <span style="color:#19177c">month</span>) (<span style="color:#19177c">month-number</span> <span style="color:#19177c">month</span>)))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">test-fun</span> (<span style="color:#008000">lambda</span> (<span style="color:#19177c">answer</span>)
</span></span><span style="display:flex;"><span>                       (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">eql</span> (<span style="color:#00f">parse-integer</span> <span style="color:#19177c">answer</span> <span style="color:#19177c">:junk-allowed</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>                                (<span style="color:#19177c">month-number</span> <span style="color:#19177c">month</span>))
</span></span><span style="display:flex;"><span>                           <span style="color:#800">t</span>))))
</span></span><span style="display:flex;"><span>      (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">question</span> <span style="color:#19177c">test-fun</span> <span style="color:#19177c">correct-response</span> <span style="color:#19177c">wrong-response</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">create-question-template</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ba2121">&#34;Which month comes next?&#34;</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">let*</span> ((<span style="color:#19177c">random-number</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> (<span style="color:#00f">random</span> <span style="color:#666">11</span>)))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">month</span> (<span style="color:#00f">assoc</span> <span style="color:#19177c">random-number</span> <span style="color:#19177c">*months*</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">next-month</span> (<span style="color:#00f">assoc</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#19177c">random-number</span>) <span style="color:#19177c">*months*</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">question</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;Which month comes after ~A?&#34;</span> (<span style="color:#19177c">month-name</span> <span style="color:#19177c">month</span>)))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">correct-response</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;That&#39;s correct!~%&#34;</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">wrong-response</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;No, that&#39;s wrong.. ~A comes after ~A.~%&#34;</span> (<span style="color:#19177c">month-name</span> <span style="color:#19177c">month</span>) (<span style="color:#19177c">month-name</span> <span style="color:#19177c">next-month</span>)))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">test-fun</span> (<span style="color:#008000">lambda</span> (<span style="color:#19177c">answer</span>)
</span></span><span style="display:flex;"><span>                       (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">string-equal</span> <span style="color:#19177c">answer</span> (<span style="color:#19177c">month-name</span> <span style="color:#19177c">next-month</span>))
</span></span><span style="display:flex;"><span>                           <span style="color:#800">t</span>))))
</span></span><span style="display:flex;"><span>      (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">question</span> <span style="color:#19177c">test-fun</span> <span style="color:#19177c">correct-response</span> <span style="color:#19177c">wrong-response</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">create-question-template</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ba2121">&#34;Which month goes before?&#34;</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">let*</span> ((<span style="color:#19177c">random-number</span> (<span style="color:#00f">+</span> <span style="color:#666">2</span> (<span style="color:#00f">random</span> <span style="color:#666">11</span>)))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">month</span> (<span style="color:#00f">assoc</span> <span style="color:#19177c">random-number</span> <span style="color:#19177c">*months*</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">last-month</span> (<span style="color:#00f">assoc</span> (<span style="color:#00f">-</span> <span style="color:#19177c">random-number</span> <span style="color:#666">1</span>) <span style="color:#19177c">*months*</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">question</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;Which month goes before ~A?&#34;</span> (<span style="color:#19177c">month-name</span> <span style="color:#19177c">month</span>)))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">correct-response</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;That&#39;s correct!~%&#34;</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">wrong-response</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;No, that&#39;s wrong.. ~A goes before ~A.~%&#34;</span> (<span style="color:#19177c">month-name</span> <span style="color:#19177c">last-month</span>) (<span style="color:#19177c">month-name</span> <span style="color:#19177c">month</span>)))
</span></span><span style="display:flex;"><span>           (<span style="color:#19177c">test-fun</span> (<span style="color:#008000">lambda</span> (<span style="color:#19177c">answer</span>)
</span></span><span style="display:flex;"><span>                       (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">string-equal</span> <span style="color:#19177c">answer</span> (<span style="color:#19177c">month-name</span> <span style="color:#19177c">last-month</span>))
</span></span><span style="display:flex;"><span>                           <span style="color:#800">t</span>))))
</span></span><span style="display:flex;"><span>      (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">question</span> <span style="color:#19177c">test-fun</span> <span style="color:#19177c">correct-response</span> <span style="color:#19177c">wrong-response</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">random-question</span> ()
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return a question at random&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">funcall</span> (<span style="color:#19177c">random-item</span> <span style="color:#19177c">*question-templates*</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">create-questions</span> (<span style="color:#19177c">number-of-questions</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return a list of questions&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">y</span> <span style="color:#19177c">from</span> <span style="color:#666">1</span> <span style="color:#19177c">to</span> <span style="color:#19177c">number-of-questions</span>
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">collect</span> (<span style="color:#19177c">random-question</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">mark-answer</span> (<span style="color:#19177c">question</span> <span style="color:#19177c">answer</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a question and answer and return t if correct.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> <span style="color:#19177c">answer</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">result</span> (<span style="color:#00f">funcall</span> (<span style="color:#00f">cadr</span> <span style="color:#19177c">question</span>) <span style="color:#19177c">answer</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#008000;font-weight:bold">if</span> <span style="color:#19177c">result</span>
</span></span><span style="display:flex;"><span>            (<span style="color:#00f">values</span> <span style="color:#19177c">result</span> (<span style="color:#00f">caddr</span> <span style="color:#19177c">question</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#00f">values</span> <span style="color:#19177c">result</span> (<span style="color:#00f">cadddr</span> <span style="color:#19177c">question</span>))))
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">values</span> <span style="color:#800">nil</span> (<span style="color:#00f">cadddr</span> <span style="color:#19177c">question</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">right-answers</span> (<span style="color:#19177c">answers</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return a count of how many answers were right.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">count</span> <span style="color:#800">nil</span> <span style="color:#19177c">answers</span> <span style="color:#19177c">:key</span> <span style="color:#19177c">&#39;not</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">ad-banner</span> ()
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;&lt;script type=&#39;text/javascript&#39;&gt;&lt;!--
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">google_ad_client = &#39;pub-6037667691060767&#39;;
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">/* Lisp days - Try 1 */
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">google_ad_slot = &#39;3672844700&#39;;
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">google_ad_width = 468;
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">google_ad_height = 15;
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">//--&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">&lt;/script&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">&lt;script type=&#39;text/javascript&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">src=&#39;http://pagead2.googlesyndication.com/pagead/show_ads.js&#39;&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">&lt;/script&gt;&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">html-prompt</span> (<span style="color:#19177c">question</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Ask the user a question through a HTML form.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-html-output-to-string</span> (<span style="color:#19177c">*standard-output*</span> <span style="color:#800">nil</span> <span style="color:#19177c">:indent</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:p</span> (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;~A&#34;</span> (<span style="color:#00f">car</span> <span style="color:#19177c">question</span>)))
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">:form</span> <span style="color:#19177c">:id</span> <span style="color:#ba2121">&#34;form&#34;</span> <span style="color:#19177c">:action</span> <span style="color:#ba2121">&#34;/months&#34;</span> <span style="color:#19177c">:method</span> <span style="color:#ba2121">&#34;post&#34;</span>
</span></span><span style="display:flex;"><span>                (<span style="color:#19177c">:input</span> <span style="color:#19177c">:id</span> <span style="color:#ba2121">&#34;guess&#34;</span> <span style="color:#19177c">:name</span> <span style="color:#ba2121">&#34;guess&#34;</span> <span style="color:#19177c">:size</span> <span style="color:#ba2121">&#34;30&#34;</span> <span style="color:#19177c">:style</span> <span style="color:#ba2121">&#34;text-align:center&#34;</span> <span style="color:#19177c">:autocomplete</span> <span style="color:#ba2121">&#34;off&#34;</span>)
</span></span><span style="display:flex;"><span>                (<span style="color:#19177c">:br</span>)
</span></span><span style="display:flex;"><span>                (<span style="color:#19177c">:input</span> <span style="color:#19177c">:type</span> <span style="color:#ba2121">&#34;submit&#34;</span> <span style="color:#19177c">:name</span> <span style="color:#ba2121">&#34;submit&#34;</span> <span style="color:#19177c">:value</span> <span style="color:#ba2121">&#34;Answer&#34;</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">snazzy-prompt</span> (<span style="color:#19177c">question</span> <span style="color:#00f;font-weight:bold">number</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-html-output-to-string</span> (<span style="color:#19177c">*standard-output*</span> <span style="color:#800">nil</span> <span style="color:#19177c">:indent</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:div</span> <span style="color:#19177c">:id</span> <span style="color:#ba2121">&#34;prompt&#34;</span>
</span></span><span style="display:flex;"><span>               (<span style="color:#19177c">:h2</span> (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;Question ~A&#34;</span> <span style="color:#00f;font-weight:bold">number</span>))
</span></span><span style="display:flex;"><span>               (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;~A&#34;</span> (<span style="color:#19177c">html-prompt</span> <span style="color:#19177c">question</span>))))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">show-score</span> (<span style="color:#19177c">answers</span> <span style="color:#19177c">questions</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Display a nice tally of the game&#39;s score&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-html-output-to-string</span> (<span style="color:#19177c">*standard-output*</span> <span style="color:#800">nil</span> <span style="color:#19177c">:indent</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">when</span> <span style="color:#19177c">answers</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">n</span> <span style="color:#19177c">from</span> (<span style="color:#00f">-</span> (<span style="color:#00f">length</span> <span style="color:#19177c">answers</span>) <span style="color:#666">1</span>) <span style="color:#19177c">downto</span> <span style="color:#666">0</span>
</span></span><span style="display:flex;"><span>         <span style="color:#008000">do</span> (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">nth</span> <span style="color:#19177c">n</span> <span style="color:#19177c">answers</span>)
</span></span><span style="display:flex;"><span>                (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:b</span> <span style="color:#ba2121">&#34;*&#34;</span>))
</span></span><span style="display:flex;"><span>                (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:b</span> <span style="color:#ba2121">&#34;.&#34;</span>)))))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">when</span> (<span style="color:#008000">and</span> <span style="color:#19177c">questions</span>
</span></span><span style="display:flex;"><span>               (<span style="color:#00f">&gt;</span> (<span style="color:#00f">length</span> <span style="color:#19177c">questions</span>) (<span style="color:#00f">length</span> <span style="color:#19177c">answers</span>)))
</span></span><span style="display:flex;"><span>      (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">n</span> <span style="color:#19177c">from</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> (<span style="color:#00f">length</span> <span style="color:#19177c">answers</span>)) <span style="color:#19177c">to</span> (<span style="color:#00f">length</span> <span style="color:#19177c">questions</span>)
</span></span><span style="display:flex;"><span>         <span style="color:#008000">do</span> (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:b</span> <span style="color:#ba2121">&#34;-&#34;</span> ))))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">when</span> (<span style="color:#008000">and</span> (<span style="color:#00f">not</span> <span style="color:#19177c">questions</span>)
</span></span><span style="display:flex;"><span>               (<span style="color:#00f">not</span> <span style="color:#19177c">answers</span>))
</span></span><span style="display:flex;"><span>      <span style="color:#ba2121">&#34;&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">months-page</span> ()
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Function to create the month game page that&#39;s displayed.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">questions</span> (<span style="color:#19177c">session-value</span> <span style="color:#ba2121">&#34;questions&#34;</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">current-question</span> (<span style="color:#19177c">session-value</span> <span style="color:#ba2121">&#34;current-question&#34;</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">guess</span> (<span style="color:#19177c">post-parameter</span> <span style="color:#ba2121">&#34;guess&#34;</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">answers</span> (<span style="color:#19177c">session-value</span> <span style="color:#ba2121">&#34;answers&#34;</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">no-cache</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">with-html-output-to-string</span> (<span style="color:#19177c">*standard-output*</span> <span style="color:#800">nil</span> <span style="color:#19177c">:indent</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#19177c">:html</span>
</span></span><span style="display:flex;"><span>       (<span style="color:#19177c">:head</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">:title</span> <span style="color:#ba2121">&#34;Months of the Year Learning Game&#34;</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">:link</span> <span style="color:#19177c">:type</span> <span style="color:#ba2121">&#34;text/css&#34;</span>
</span></span><span style="display:flex;"><span>               <span style="color:#19177c">:rel</span> <span style="color:#ba2121">&#34;stylesheet&#34;</span>
</span></span><span style="display:flex;"><span>               <span style="color:#19177c">:href</span> <span style="color:#ba2121">&#34;/lisp/template.css&#34;</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">:script</span> <span style="color:#19177c">:type</span> <span style="color:#ba2121">&#34;text/javascript&#34;</span>
</span></span><span style="display:flex;"><span>                 <span style="color:#19177c">:src</span> <span style="color:#ba2121">&#34;/lisp/include.js&#34;</span>))
</span></span><span style="display:flex;"><span>       (<span style="color:#19177c">:body</span> <span style="color:#19177c">:onload</span> <span style="color:#ba2121">&#34;setFocus();&#34;</span>
</span></span><span style="display:flex;"><span>              (<span style="color:#19177c">:center</span>
</span></span><span style="display:flex;"><span>               (<span style="color:#19177c">:p</span> (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;~A&#34;</span> (<span style="color:#19177c">ad-banner</span>)))
</span></span><span style="display:flex;"><span>               (<span style="color:#19177c">:h1</span> <span style="color:#ba2121">&#34;Months of the Year Learning Game&#34;</span>)
</span></span><span style="display:flex;"><span>               (<span style="color:#19177c">:br</span>)
</span></span><span style="display:flex;"><span>               (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">not</span> <span style="color:#19177c">questions</span>)      <span style="color:#408080;font-style:italic">; New Game&#39;s starting</span>
</span></span><span style="display:flex;"><span>                   (<span style="color:#008000;font-weight:bold">progn</span>
</span></span><span style="display:flex;"><span>                     (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:p</span> (<span style="color:#19177c">:b</span> <span style="color:#ba2121">&#34;Welcome to the Month Learning Game!&#34;</span>))
</span></span><span style="display:flex;"><span>                          (<span style="color:#19177c">:p</span> (<span style="color:#19177c">:i</span> <span style="color:#ba2121">&#34;Try your best to answer 10 questions about the different Months&#34;</span>))
</span></span><span style="display:flex;"><span>                          (<span style="color:#19177c">:br</span>))
</span></span><span style="display:flex;"><span>                     (<span style="color:#19177c">start-session</span>)
</span></span><span style="display:flex;"><span>                     (<span style="color:#008000">setf</span> (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:current-question</span>) <span style="color:#666">0</span>)
</span></span><span style="display:flex;"><span>                     (<span style="color:#008000">setf</span> (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:answers</span>) <span style="color:#800">nil</span>)
</span></span><span style="display:flex;"><span>                     (<span style="color:#008000">setf</span> (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:questions</span>) (<span style="color:#19177c">create-questions</span> <span style="color:#666">10</span>))
</span></span><span style="display:flex;"><span>                     (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;~A&#34;</span> (<span style="color:#19177c">snazzy-prompt</span> (<span style="color:#00f">nth</span> (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:current-question</span>) (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:questions</span>)) <span style="color:#666">1</span>)))
</span></span><span style="display:flex;"><span>                   (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">not</span> (<span style="color:#00f">&lt;</span> <span style="color:#666">0</span> (<span style="color:#00f">length</span> <span style="color:#19177c">guess</span>)))
</span></span><span style="display:flex;"><span>                       (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:p</span> (<span style="color:#19177c">:b</span> <span style="color:#ba2121">&#34;Please Answer!&#34;</span>))
</span></span><span style="display:flex;"><span>                            (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;~A&#34;</span> (<span style="color:#19177c">snazzy-prompt</span> (<span style="color:#00f">nth</span> <span style="color:#19177c">current-question</span> <span style="color:#19177c">questions</span>) (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#19177c">current-question</span>))))
</span></span><span style="display:flex;"><span>                       (<span style="color:#008000">multiple-value-bind</span> (<span style="color:#19177c">result</span> <span style="color:#19177c">output</span>) <span style="color:#408080;font-style:italic">; Game&#39;s in progress</span>
</span></span><span style="display:flex;"><span>                           (<span style="color:#19177c">mark-answer</span> (<span style="color:#00f">nth</span> <span style="color:#19177c">current-question</span> <span style="color:#19177c">questions</span>) <span style="color:#19177c">guess</span>)
</span></span><span style="display:flex;"><span>                         (<span style="color:#008000">push</span> <span style="color:#19177c">result</span> (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:answers</span>))
</span></span><span style="display:flex;"><span>                         (<span style="color:#008000">push</span> <span style="color:#19177c">result</span> <span style="color:#19177c">answers</span>)
</span></span><span style="display:flex;"><span>                         (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:p</span> (<span style="color:#19177c">:i</span> (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;&#39;~A ~A&#39;&#34;</span> (<span style="color:#00f">car</span> (<span style="color:#00f">nth</span> <span style="color:#19177c">current-question</span> <span style="color:#19177c">questions</span>)) <span style="color:#19177c">guess</span>)))
</span></span><span style="display:flex;"><span>                              (<span style="color:#19177c">:p</span> (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;~A&#34;</span>  <span style="color:#19177c">output</span>)))
</span></span><span style="display:flex;"><span>                         (<span style="color:#008000">incf</span> <span style="color:#19177c">current-question</span>)
</span></span><span style="display:flex;"><span>                         (<span style="color:#008000">incf</span> (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:current-question</span>))
</span></span><span style="display:flex;"><span>                         (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">current-question</span> (<span style="color:#00f">length</span> <span style="color:#19177c">questions</span>)) <span style="color:#408080;font-style:italic">; Game&#39;s just finished</span>
</span></span><span style="display:flex;"><span>                             (<span style="color:#008000;font-weight:bold">progn</span>
</span></span><span style="display:flex;"><span>                               (<span style="color:#19177c">htm</span> (<span style="color:#19177c">:p</span> (<span style="color:#19177c">:b</span> (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;** You scored ~A out of ~A **&#34;</span>
</span></span><span style="display:flex;"><span>                                                 (<span style="color:#19177c">right-answers</span> <span style="color:#19177c">answers</span>)
</span></span><span style="display:flex;"><span>                                                 (<span style="color:#00f">length</span> <span style="color:#19177c">questions</span>))))
</span></span><span style="display:flex;"><span>                                    (<span style="color:#19177c">:p</span> (<span style="color:#19177c">:a</span> <span style="color:#19177c">:href</span> <span style="color:#ba2121">&#34;/months&#34;</span> <span style="color:#ba2121">&#34;Play again?&#34;</span>)))
</span></span><span style="display:flex;"><span>                               (<span style="color:#19177c">delete-session-value</span> <span style="color:#19177c">:questions</span>)
</span></span><span style="display:flex;"><span>                               (<span style="color:#19177c">delete-session-value</span> <span style="color:#19177c">:current-question</span>)
</span></span><span style="display:flex;"><span>                               (<span style="color:#19177c">delete-session-value</span> <span style="color:#19177c">:answers</span>))
</span></span><span style="display:flex;"><span>                                        <span style="color:#408080;font-style:italic">;Game&#39;s still going, ask another question!</span>
</span></span><span style="display:flex;"><span>                             (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;~A&#34;</span> (<span style="color:#19177c">snazzy-prompt</span> (<span style="color:#00f">nth</span> <span style="color:#19177c">current-question</span> <span style="color:#19177c">questions</span>) (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#19177c">current-question</span>)))))))
</span></span><span style="display:flex;"><span>               (<span style="color:#19177c">fmt</span> <span style="color:#ba2121">&#34;~A&#34;</span> (<span style="color:#19177c">show-score</span> (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:answers</span>)
</span></span><span style="display:flex;"><span>                                     (<span style="color:#19177c">session-value</span> <span style="color:#19177c">:questions</span>)))))))))</span></span></code></pre></div>
<ul>
<li>It&rsquo;s reasonably flexible already, but I want to be able to make separate quizzes without rewriting stuff.</li>
<li>I&rsquo;m also not sure about how I wrote the game logic inside the <code>months-page</code> function. It&rsquo;s messy, but I don&rsquo;t see how else to do it.</li>
<li>I don&rsquo;t like how creating the question templates looks so ugly.</li>
</ul>
<p>I&rsquo;m a bit stuck with how to improve the code now, could anyone give me any pointers?</p>
<p>Cheers, Dave.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/10/08/lisp-web-development-environment-setup</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/10/08/lisp-web-development-environment-setup" />
    <title>Setting up a Lisp web development environment</title>
    <published>2008-10-08T00:00:00+00:00</published>
    <updated>2008-10-08T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Here&rsquo;s a guide to take a fresh Debian stable (Etch) install and turn it into a viable Lisp web development environment. (Including Emacs22, SBCL, Slime, Hunchentoot, Apache2 all working together from boot)</p>
<p>Please note that this guide assumes you&rsquo;re using a fresh installation of Debian stable.</p>
<p>All the commands assume you are logged in as root on your soon-to-be web server.</p>
<p>Installation Steps</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Install Emacs 22</span>
</span></span><span style="display:flex;"><span><span style="color:#008000">echo</span> deb http://hype.sourceforge.jp/f etch backports &gt;&gt; /etc/apt/sources.list
</span></span><span style="display:flex;"><span>wget http://hype.sourceforge.jp/A7F20B7E.gpg -O- | apt-key add -
</span></span><span style="display:flex;"><span>apt-get update
</span></span><span style="display:flex;"><span>apt-get install emacs22
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Install the other required packages</span>
</span></span><span style="display:flex;"><span>apt-get install darcs cvs subversion curl git-core cogito sbcl make gcc build-essential detachtty
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Grab clbuild</span>
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> /usr/src
</span></span><span style="display:flex;"><span>darcs get http://common-lisp.net/project/clbuild/clbuild
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> clbuild <span style="color:#666">&amp;&amp;</span> chmod +x clbuild
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Install the different bits and bobs with clbuild</span>
</span></span><span style="display:flex;"><span>./clbuild update sbcl
</span></span><span style="display:flex;"><span>./clbuild compile-implementation sbcl
</span></span><span style="display:flex;"><span><span style="color:#008000">echo</span> cl-unicode get_darcs http://common-lisp.net/~loliveira/ediware/cl-unicode &gt;&gt; projects
</span></span><span style="display:flex;"><span><span style="color:#008000">echo</span> metabang-bind get_darcs http://common-lisp.net/project/metabang-bind &gt;&gt; projects
</span></span><span style="display:flex;"><span><span style="color:#008000">echo</span> stefil get_darcs http://common-lisp.net/project/stefil/darcs/stefil &gt;&gt; projects
</span></span><span style="display:flex;"><span><span style="color:#008000">echo</span> cffi get_darcs http://common-lisp.net/project/cffi/darcs/cffi &gt;&gt; projects
</span></span><span style="display:flex;"><span>./clbuild build cl-unicode cl-ppcre hunchentoot vecto cl-who slime metabang-bind iterate stefil babel cffi
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Make a Lisp image</span>
</span></span><span style="display:flex;"><span>./clbuild dumpcore --installed
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Setup Lisp to start on boot</span>
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> /etc/init.d
</span></span><span style="display:flex;"><span>wget http://www.kzar.co.uk/lisp-install/lisp-boot
</span></span><span style="display:flex;"><span>chmod a+x lisp-boot
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> /etc/rc2.d
</span></span><span style="display:flex;"><span>ln -s ../init.d/lisp-boot S98lispboot
</span></span><span style="display:flex;"><span>mkdir /var/run/lisp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Add the lisp user for Lisp to run as</span>
</span></span><span style="display:flex;"><span>adduser lisp-user
</span></span><span style="display:flex;"><span>touch /var/log/detachtty.log
</span></span><span style="display:flex;"><span>touch /var/log/lisp-app-dribble
</span></span><span style="display:flex;"><span>chown lisp-user:lisp-user /var/log/detachtty.log
</span></span><span style="display:flex;"><span>chown lisp-user:lisp-user -R /var/run/lisp
</span></span><span style="display:flex;"><span>chown lisp-user:lisp-user /var/log/lisp-app-dribble
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Make it so Lisp will start the webserver and swank server each time</span>
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> /usr/src/clbuild/
</span></span><span style="display:flex;"><span>mkdir /var/lisp <span style="color:#666">&amp;&amp;</span> <span style="color:#008000">cd</span> /var/lisp
</span></span><span style="display:flex;"><span>wget http://www.kzar.co.uk/lisp-install/server.lisp
</span></span><span style="display:flex;"><span>mkdir images <span style="color:#666">&amp;&amp;</span> <span style="color:#008000">cd</span> images
</span></span><span style="display:flex;"><span>wget http://www.lisperati.com/lisplogo_alien_128.png
</span></span><span style="display:flex;"><span>chmod g+w -R /var/lisp
</span></span><span style="display:flex;"><span>chown -R lisp-user:lisp-user /var/lisp
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Grab Apache2 and set it up with modlisp</span>
</span></span><span style="display:flex;"><span>apt-get install apache2 cl-modlisp libapache-mod-lisp apache2-threaded-dev
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> /etc/apache2/sites-available/
</span></span><span style="display:flex;"><span>rm default <span style="color:#666">&amp;&amp;</span> wget http://www.kzar.co.uk/lisp-install/default
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> /etc/apache2/mods-enabled
</span></span><span style="display:flex;"><span>wget http://www.kzar.co.uk/lisp-install/lisp.load
</span></span><span style="display:flex;"><span>wget http://www.kzar.co.uk/lisp-install/lisp.conf
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> /usr/lib/apache2/modules
</span></span><span style="display:flex;"><span>wget http://www.kzar.co.uk/lisp-install/mod_lisp2.so
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Get rid of the old SBCL that comes with Debian (Thanks Todd)</span>
</span></span><span style="display:flex;"><span>apt-get remove sbcl
</span></span><span style="display:flex;"><span><span style="color:#008000">echo</span> <span style="color:#19177c">SBCL_HOME</span><span style="color:#666">=</span>/usr/src/clbuild/target/lib/sbcl &gt;&gt; /etc/profile
</span></span><span style="display:flex;"><span>ln -s /usr/src/clbuild/target/bin/sbcl /usr/bin/sbcl
</span></span><span style="display:flex;"><span>ln -s /usr/src/clbuild/target/lib/sbcl /usr/local/lib/sbcl
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> /usr/src/clbuild/target/lib/sbcl <span style="color:#666">&amp;&amp;</span> rm -r site-system
</span></span><span style="display:flex;"><span>ln -s /usr/lib/sbcl/* .
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Start it all running</span>
</span></span><span style="display:flex;"><span>/etc/init.d/apache2 restart
</span></span><span style="display:flex;"><span>/etc/init.d/lisp-boot start
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic"># Set up Emacs (Run as the user who will use Emacs)</span>
</span></span><span style="display:flex;"><span><span style="color:#008000">cd</span> ~ <span style="color:#666">&amp;&amp;</span> wget http://www.kzar.co.uk/lisp-install/.emacs</span></span></code></pre></div>
<p>Usage</p>
<ul>
<li>Browse to <code>http://SERVERNAME/lisp</code> to view your website.</li>
<li>Type <code>emacs /var/lisp/server.lisp</code> to start editing the little example page.</li>
<li>Type <code>M-x slime-connect RET RET</code> in Emacs to connect to the running Lisp REPL</li>
</ul>
<p>Notes</p>
<ul>
<li>Add your user to the <code>lisp-user</code> group to edit the files</li>
<li>Make sure you have downloaded the .emacs for your user so <code>slime-connect</code> works properly</li>
</ul>
<p>Leave a comment if you hit a problem and I&rsquo;ll try and help - Dave.</p>
<p>Edit: Thanks to Todd for some good feedback, I have added some extra commands that help to get rid of the outdated version of SBCL which comes with Debian, while still making use of Debian&rsquo;s cl package. (That&rsquo;s the hope anyway, let me know if it worked for you / if there&rsquo;s a better way to do that part)</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/09/27/mu-puzzle-solver</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/09/27/mu-puzzle-solver" />
    <title>MU puzzle &#39;solver&#39;</title>
    <published>2008-09-27T00:00:00+00:00</published>
    <updated>2008-09-27T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I was reading Godel, Escher, Bach: An Eternal Golden Braid on the train and when reading about the MU puzzle I decided to give it a go. I wrote most of the code on my lil&rsquo; eeepc, but I did get a bit stuck with the recursion, it was hard to get the <code>apply-rules</code> function to return the answer once it found a path to &ldquo;MU&rdquo;.</p>
<p>In the end with some help from the good folks in #emacs and #lisp I had it working here&rsquo;s the code:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*path*</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">all-matches</span> (<span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">pattern</span> <span style="color:#008000;font-weight:bold">&amp;key</span> (<span style="color:#19177c">start</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return a list of positions for all matches of the pattern in the string&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">match</span> (<span style="color:#00f">search</span> <span style="color:#19177c">pattern</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">:start2</span> <span style="color:#19177c">start</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">when</span> <span style="color:#19177c">match</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">append</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">match</span>)
</span></span><span style="display:flex;"><span>              (<span style="color:#19177c">all-matches</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">pattern</span> <span style="color:#19177c">:start</span> (<span style="color:#00f">+</span> <span style="color:#19177c">match</span> <span style="color:#666">1</span>))))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">list-if</span> (<span style="color:#19177c">x</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">when</span> <span style="color:#19177c">x</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">x</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">replace-substr</span> (<span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">start</span> <span style="color:#19177c">end</span> <span style="color:#19177c">new</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">concatenate</span> <span style="color:#19177c">&#39;string</span> (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#666">0</span> <span style="color:#19177c">start</span>) <span style="color:#19177c">new</span>
</span></span><span style="display:flex;"><span>               (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">end</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">replace-all-once</span> (<span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">pattern</span> <span style="color:#19177c">new</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">matches</span> (<span style="color:#19177c">all-matches</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">pattern</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">remove-duplicates</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">match</span> <span style="color:#19177c">in</span> <span style="color:#19177c">matches</span>
</span></span><span style="display:flex;"><span>        <span style="color:#19177c">collect</span> (<span style="color:#19177c">replace-substr</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">match</span> (<span style="color:#00f">+</span> <span style="color:#19177c">match</span> (<span style="color:#00f">length</span> <span style="color:#19177c">pattern</span>)) <span style="color:#19177c">new</span>))
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">:test</span> <span style="color:#00f">#&#39;equal</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">string-last</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#00f">aref</span> <span style="color:#00f;font-weight:bold">string</span> (<span style="color:#00f">1-</span> (<span style="color:#00f">length</span> <span style="color:#00f;font-weight:bold">string</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">rule-1</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">when</span> (<span style="color:#00f">eql</span> (<span style="color:#19177c">string-last</span> <span style="color:#00f;font-weight:bold">string</span>) <span style="color:#ba2121">#\I</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">concatenate</span> <span style="color:#19177c">&#39;string</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#ba2121">&#34;U&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">rule-2</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">when</span> (<span style="color:#00f">eql</span> (<span style="color:#00f">aref</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#666">0</span>) <span style="color:#ba2121">#\M</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">&gt;</span> (<span style="color:#00f">length</span> <span style="color:#00f;font-weight:bold">string</span>) <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">concatenate</span> <span style="color:#19177c">&#39;string</span> <span style="color:#00f;font-weight:bold">string</span> (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>        <span style="color:#00f;font-weight:bold">string</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">rule-3</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">replace-all-once</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#ba2121">&#34;III&#34;</span> <span style="color:#ba2121">&#34;U&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">rule-4</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">replace-all-once</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#ba2121">&#34;UU&#34;</span> <span style="color:#ba2121">&#34;&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">apply-rules</span> (<span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">depth</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return a list of axioms returned by the various rules&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">or</span> (<span style="color:#00f">&lt;</span> <span style="color:#19177c">depth</span> <span style="color:#666">1</span>) <span style="color:#19177c">*path*</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#800">nil</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">equal</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#ba2121">&#34;MU&#34;</span>)
</span></span><span style="display:flex;"><span>          (<span style="color:#008000">push</span> <span style="color:#ba2121">&#34;MU&#34;</span> <span style="color:#19177c">*path*</span>)
</span></span><span style="display:flex;"><span>          (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">results</span> (<span style="color:#00f">append</span>
</span></span><span style="display:flex;"><span>                          (<span style="color:#19177c">list-if</span> (<span style="color:#19177c">rule-1</span> <span style="color:#00f;font-weight:bold">string</span>))
</span></span><span style="display:flex;"><span>                          (<span style="color:#19177c">list-if</span> (<span style="color:#19177c">rule-2</span> <span style="color:#00f;font-weight:bold">string</span>))
</span></span><span style="display:flex;"><span>                          (<span style="color:#19177c">rule-3</span> <span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>                          (<span style="color:#19177c">rule-4</span> <span style="color:#00f;font-weight:bold">string</span>))))
</span></span><span style="display:flex;"><span>            (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">match</span> <span style="color:#19177c">in</span> <span style="color:#19177c">results</span>
</span></span><span style="display:flex;"><span>               <span style="color:#19177c">collect</span> (<span style="color:#00f">append</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">match</span>) (<span style="color:#19177c">apply-rules</span> <span style="color:#19177c">match</span> (<span style="color:#00f">1-</span> <span style="color:#19177c">depth</span>)))
</span></span><span style="display:flex;"><span>               <span style="color:#008000">do</span> (<span style="color:#008000">when</span> (<span style="color:#008000">and</span> <span style="color:#19177c">match</span> (<span style="color:#00f">equal</span> (<span style="color:#00f">last</span> <span style="color:#19177c">*path*</span>) (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">match</span>)))
</span></span><span style="display:flex;"><span>                    (<span style="color:#008000">push</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">*path*</span>)))))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">find-mu</span> (<span style="color:#19177c">depth</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> (<span style="color:#19177c">tree</span> <span style="color:#19177c">*path*</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">setf</span> <span style="color:#19177c">tree</span> (<span style="color:#19177c">apply-rules</span> <span style="color:#ba2121">&#34;MI&#34;</span> <span style="color:#19177c">depth</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> <span style="color:#19177c">*path*</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">format</span> <span style="color:#800">t</span> <span style="color:#ba2121">&#34;The answer is: ~a. ~%&#34;</span> <span style="color:#19177c">*path*</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">format</span> <span style="color:#800">t</span> <span style="color:#ba2121">&#34;Couldn&#39;t solve it by going through ~a levels of possibilities. ~%  Tree:~A&#34;</span> <span style="color:#19177c">depth</span> <span style="color:#19177c">tree</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#19177c">find-mu</span> <span style="color:#666">5</span>)</span></span></code></pre></div>
<p>Now when Emacs ground to a halt trying to solve the puzzle, beach pointed out that it was actually impossible and it&rsquo;s explained later in the book&hellip; whoops! Anyway, I thought it was a nifty little program so I thought I&rsquo;d post it anyway.</p>
<p>(Try replacing &ldquo;MU&rdquo; with something that is possible to find to see it in action.)</p>
<p>Cheers, Dave.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/09/21/parsing-date-strings-in-lisp</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/09/21/parsing-date-strings-in-lisp" />
    <title>Parsing date strings in Lisp</title>
    <published>2008-09-21T00:00:00+00:00</published>
    <updated>2008-09-21T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Although quite pleased with my last experiment getting a web interface working I wanted to make it more powerful.</p>
<p>I decided the best way to go to avoid complexity was to parse input from the user so you could enter a date or a date range in any format you wanted, and the program would hopefully understand what you meant. To do this I started to learn how to basic language parsers work, and it seemed quite simple, so I gave it a go.</p>
<p>It turned out to be a good challenge. I think I now finally &lsquo;get&rsquo; Lisp macros now as well thanks to the guys in #lisp. It&rsquo;s working pretty well. You can give it stuff like &ldquo;next week&rdquo; or &ldquo;last month&rdquo; or &ldquo;16th September 2008 to today&rdquo; or &ldquo;12 03 2009&rdquo; etc.</p>
<p>Here&rsquo;s the code so far:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#00f">require</span> <span style="color:#19177c">:cl-ppcre</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defpackage</span> <span style="color:#19177c">:disco-parser</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">:use</span> <span style="color:#19177c">:asdf</span> <span style="color:#19177c">:asdf-install</span> <span style="color:#19177c">:cl</span> <span style="color:#19177c">:cl-ppcre</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">:export</span> <span style="color:#19177c">:make-rule</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:craft-rule</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:parse</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:split</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">in-package</span> <span style="color:#19177c">:disco-parser</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">do-rule-fun</span> (<span style="color:#19177c">name</span> <span style="color:#19177c">regexp</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#00f;font-weight:bold">function</span>)
</span></span><span style="display:flex;"><span><span style="color:#ba2121">&#34;Search the string for the regexp and return the result of running the function on the matches as a token list&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">matches</span> (<span style="color:#19177c">all-matches</span> <span style="color:#19177c">regexp</span> <span style="color:#00f;font-weight:bold">string</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> (<span style="color:#19177c">start</span> <span style="color:#19177c">end</span>) <span style="color:#19177c">on</span> <span style="color:#19177c">matches</span> <span style="color:#19177c">by</span> <span style="color:#00f">#&#39;cddr</span>
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">collect</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">name</span> <span style="color:#19177c">start</span> <span style="color:#19177c">end</span>
</span></span><span style="display:flex;"><span>                     (<span style="color:#00f">funcall</span> <span style="color:#00f;font-weight:bold">function</span> (<span style="color:#00f">format</span> <span style="color:#800">nil</span> (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">start</span> <span style="color:#19177c">end</span>)))))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defmacro</span> <span style="color:#19177c">make-rule</span> (<span style="color:#19177c">name</span> <span style="color:#19177c">regexp</span> <span style="color:#008000;font-weight:bold">&amp;body</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return a function that calls do-rule-fun taking a string to search as it&#39;s parameter. AKA return a regexp rule&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">`</span>(<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">scanner</span> (<span style="color:#19177c">create-scanner</span> <span style="color:#666">,</span><span style="color:#19177c">regexp</span>)))
</span></span><span style="display:flex;"><span>     (<span style="color:#008000">lambda</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#19177c">do-rule-fun</span> <span style="color:#19177c">&#39;,name</span> <span style="color:#19177c">scanner</span> <span style="color:#00f;font-weight:bold">string</span> (<span style="color:#008000">lambda</span> (<span style="color:#666">,</span><span style="color:#19177c">name</span>) <span style="color:#666">,@</span><span style="color:#19177c">body</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">flatten</span> (<span style="color:#19177c">tree</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Takes a tree and returns the tree flattened it into a list&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">when</span> <span style="color:#19177c">tree</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">consp</span> <span style="color:#19177c">tree</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#00f">append</span> (<span style="color:#19177c">flatten</span> (<span style="color:#00f">car</span> <span style="color:#19177c">tree</span>)) (<span style="color:#19177c">flatten</span> (<span style="color:#00f">cdr</span> <span style="color:#19177c">tree</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#00f;font-weight:bold">cons</span> <span style="color:#19177c">tree</span> <span style="color:#800">nil</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defmacro</span> <span style="color:#19177c">with-token</span> ((<span style="color:#008000;font-weight:bold">&amp;key</span> (<span style="color:#008000;font-weight:bold">type</span> <span style="color:#800">nil</span>) (<span style="color:#19177c">start</span> <span style="color:#800">nil</span>) (<span style="color:#19177c">end</span> <span style="color:#800">nil</span>) (<span style="color:#19177c">value</span> <span style="color:#800">nil</span>))
</span></span><span style="display:flex;"><span>                      <span style="color:#19177c">token</span> <span style="color:#008000;font-weight:bold">&amp;body</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a token and some keywords and put the parts of the token into the keywords&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">`</span>(<span style="color:#008000;font-weight:bold">let</span> (<span style="color:#666">,@</span>(<span style="color:#008000">when</span> <span style="color:#008000;font-weight:bold">type</span> <span style="color:#666">`</span>((<span style="color:#666">,</span><span style="color:#008000;font-weight:bold">type</span> (<span style="color:#00f">first</span> <span style="color:#666">,</span><span style="color:#19177c">token</span>))))
</span></span><span style="display:flex;"><span>        <span style="color:#666">,@</span>(<span style="color:#008000">when</span> <span style="color:#19177c">start</span> <span style="color:#666">`</span>((<span style="color:#666">,</span><span style="color:#19177c">start</span> (<span style="color:#00f">second</span> <span style="color:#666">,</span><span style="color:#19177c">token</span>))))
</span></span><span style="display:flex;"><span>        <span style="color:#666">,@</span>(<span style="color:#008000">when</span> <span style="color:#19177c">end</span> <span style="color:#666">`</span>((<span style="color:#666">,</span><span style="color:#19177c">end</span> (<span style="color:#00f">third</span> <span style="color:#666">,</span><span style="color:#19177c">token</span>))))
</span></span><span style="display:flex;"><span>        <span style="color:#666">,@</span>(<span style="color:#008000">when</span> <span style="color:#19177c">value</span> <span style="color:#666">`</span>((<span style="color:#666">,</span><span style="color:#19177c">value</span> (<span style="color:#00f">fourth</span> <span style="color:#666">,</span><span style="color:#19177c">token</span>)))))
</span></span><span style="display:flex;"><span>    <span style="color:#666">,@</span><span style="color:#19177c">body</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">clash?</span> (<span style="color:#19177c">existing-tokens</span> <span style="color:#19177c">new-token</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">when</span> <span style="color:#19177c">existing-tokens</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">with-token</span> (<span style="color:#19177c">:start</span> <span style="color:#19177c">new-start</span> <span style="color:#19177c">:end</span> <span style="color:#19177c">new-end</span>) <span style="color:#19177c">new-token</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#19177c">with-token</span> (<span style="color:#19177c">:start</span> <span style="color:#19177c">old-start</span> <span style="color:#19177c">:end</span> <span style="color:#19177c">old-end</span>) (<span style="color:#00f">car</span> <span style="color:#19177c">existing-tokens</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">or</span> (<span style="color:#00f">&gt;</span> <span style="color:#19177c">new-start</span> <span style="color:#19177c">old-end</span>)
</span></span><span style="display:flex;"><span>                (<span style="color:#00f">&lt;</span> <span style="color:#19177c">new-end</span> <span style="color:#19177c">old-start</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">consp</span> (<span style="color:#00f">cdr</span> <span style="color:#19177c">existing-tokens</span>))
</span></span><span style="display:flex;"><span>                (<span style="color:#19177c">clash?</span> (<span style="color:#00f">cdr</span> <span style="color:#19177c">existing-tokens</span>) <span style="color:#19177c">new-token</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#800">nil</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#800">t</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">remove-clashing-tokens</span> (<span style="color:#19177c">existing-tokens</span> <span style="color:#19177c">new-tokens</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return the new-tokens list of tokens minus any that clashed with existing tokens&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">good-new-tokens</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">dolist</span> (<span style="color:#19177c">new-token</span> <span style="color:#19177c">new-tokens</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">not</span> (<span style="color:#19177c">clash?</span> <span style="color:#19177c">existing-tokens</span> <span style="color:#19177c">new-token</span>))
</span></span><span style="display:flex;"><span>          (<span style="color:#008000">push</span> <span style="color:#19177c">new-token</span> <span style="color:#19177c">good-new-tokens</span>)))
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">good-new-tokens</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">tokenise</span> (<span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">token-rules</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Takes a string and a list of token-rules and returns a list of tokens&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> (<span style="color:#19177c">tokens</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">dolist</span> (<span style="color:#19177c">rule</span> <span style="color:#19177c">token-rules</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#008000">setf</span> <span style="color:#19177c">tokens</span> (<span style="color:#00f">append</span> (<span style="color:#19177c">remove-clashing-tokens</span> <span style="color:#19177c">tokens</span> (<span style="color:#00f">funcall</span> <span style="color:#19177c">rule</span> <span style="color:#00f;font-weight:bold">string</span>))
</span></span><span style="display:flex;"><span>                           <span style="color:#19177c">tokens</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">sort</span> <span style="color:#19177c">tokens</span> <span style="color:#00f">#&#39;&lt;</span> <span style="color:#19177c">:key</span> <span style="color:#00f">#&#39;cadr</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">find-pattern</span> (<span style="color:#19177c">pattern</span> <span style="color:#19177c">token-list</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a token list and a pattern in it to match and it will return a list of matches&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> (<span style="color:#19177c">match-position</span> (<span style="color:#19177c">start</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">while</span> (<span style="color:#008000">setf</span> <span style="color:#19177c">match-position</span> (<span style="color:#00f">search</span> <span style="color:#19177c">pattern</span> (<span style="color:#00f">mapcar</span> <span style="color:#00f">#&#39;car</span> <span style="color:#19177c">token-list</span>) <span style="color:#19177c">:start2</span> <span style="color:#19177c">start</span>))
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">setf</span> <span style="color:#19177c">start</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#19177c">match-position</span>))
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">collect</span> (<span style="color:#008000">loop</span> <span style="color:#19177c">repeat</span> (<span style="color:#00f">length</span> <span style="color:#19177c">pattern</span>)
</span></span><span style="display:flex;"><span>                  <span style="color:#19177c">for</span> <span style="color:#19177c">x</span> <span style="color:#00f">=</span> <span style="color:#19177c">match-position</span> <span style="color:#19177c">then</span> (<span style="color:#00f">+</span> <span style="color:#19177c">x</span> <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>                  <span style="color:#19177c">collect</span> (<span style="color:#00f">nth</span> <span style="color:#19177c">x</span> <span style="color:#19177c">token-list</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">apply-rule-fun</span> (<span style="color:#19177c">pattern</span> <span style="color:#19177c">name</span> <span style="color:#19177c">func</span> <span style="color:#19177c">token-list</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a rule and &#39;run&#39; it&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">new-tokens</span>
</span></span><span style="display:flex;"><span>         (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">match</span> <span style="color:#19177c">in</span> (<span style="color:#19177c">find-pattern</span> <span style="color:#19177c">pattern</span> <span style="color:#19177c">token-list</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#19177c">collect</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">name</span>
</span></span><span style="display:flex;"><span>                          (<span style="color:#00f">cadr</span> (<span style="color:#00f">car</span> <span style="color:#19177c">match</span>))
</span></span><span style="display:flex;"><span>                          (<span style="color:#00f">third</span> (<span style="color:#00f">car</span> (<span style="color:#00f">last</span> <span style="color:#19177c">match</span>)))
</span></span><span style="display:flex;"><span>                          (<span style="color:#00f">apply</span> <span style="color:#19177c">func</span> (<span style="color:#00f">mapcar</span> <span style="color:#00f">#&#39;fourth</span> <span style="color:#19177c">match</span>))))))
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">sort</span> (<span style="color:#00f">append</span> (<span style="color:#19177c">remove-clashing-tokens</span> <span style="color:#19177c">new-tokens</span> <span style="color:#19177c">token-list</span>) <span style="color:#19177c">new-tokens</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#00f">#&#39;&lt;</span> <span style="color:#19177c">:key</span> <span style="color:#00f">#&#39;cadr</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defmacro</span> <span style="color:#19177c">craft-rule</span> (<span style="color:#19177c">pattern</span> <span style="color:#19177c">name</span> <span style="color:#008000;font-weight:bold">&amp;body</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Macro that should make an easier interface to use than apply-rule-fun. Give it all the info apart from the list of tokens and it returns a function that will take the list of tokens&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> (<span style="color:#19177c">key</span> <span style="color:#19177c">val</span>) <span style="color:#19177c">on</span> <span style="color:#19177c">pattern</span> <span style="color:#19177c">by</span> <span style="color:#00f">#&#39;cddr</span>
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">collect</span> <span style="color:#19177c">key</span> <span style="color:#19177c">into</span> <span style="color:#19177c">keys</span>
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">collect</span> <span style="color:#19177c">val</span> <span style="color:#19177c">into</span> <span style="color:#19177c">vals</span>
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">finally</span> (<span style="color:#008000">return</span>
</span></span><span style="display:flex;"><span>               <span style="color:#666">`</span>(<span style="color:#008000">lambda</span> (<span style="color:#19177c">token-list</span>)
</span></span><span style="display:flex;"><span>                  (<span style="color:#19177c">apply-rule-fun</span> <span style="color:#19177c">&#39;,keys</span> <span style="color:#19177c">&#39;,name</span>
</span></span><span style="display:flex;"><span>                                  (<span style="color:#008000">lambda</span> <span style="color:#666">,</span><span style="color:#19177c">vals</span> <span style="color:#666">,@</span><span style="color:#19177c">body</span>) <span style="color:#19177c">token-list</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">apply-logic-rules</span> (<span style="color:#19177c">token-list</span> <span style="color:#19177c">rules</span>)
</span></span><span style="display:flex;"><span><span style="color:#ba2121">&#34;Take some tokens and a list of logic rules and return the new token list&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">tokens</span> <span style="color:#19177c">token-list</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">rule</span> <span style="color:#19177c">in</span> <span style="color:#19177c">rules</span>
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">setf</span> <span style="color:#19177c">tokens</span> (<span style="color:#00f">apply</span> <span style="color:#19177c">rule</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">tokens</span>))))
</span></span><span style="display:flex;"><span>    <span style="color:#19177c">tokens</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">parse</span> (<span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">token-rules</span> <span style="color:#19177c">logic-rules</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a string, the token and logic rules and return the tokens&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">apply-logic-rules</span> (<span style="color:#19177c">tokenise</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">token-rules</span>) <span style="color:#19177c">logic-rules</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;; Todo</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Sort out the naming of the various functions and rules</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; e.g. what&#39;s the difference from a logic and token rule!?</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">;</span>
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Write code to check grammatical correctness</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*date-token-rules*</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f;font-weight:bold">list</span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day</span> <span style="color:#ba2121">&#34;[0-9]+rd&#34;</span> (<span style="color:#00f">parse-integer</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:junk-allowed</span> <span style="color:#800">t</span>))
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day</span> <span style="color:#ba2121">&#34;[0-9]+th&#34;</span> (<span style="color:#00f">parse-integer</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:junk-allowed</span> <span style="color:#800">t</span>))
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day</span> <span style="color:#ba2121">&#34;[0-9]+st&#34;</span> (<span style="color:#00f">parse-integer</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:junk-allowed</span> <span style="color:#800">t</span>))
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day-of-week</span> <span style="color:#ba2121">&#34;(?i:Mon(day)?)&#34;</span> <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day-of-week</span> <span style="color:#ba2121">&#34;(?i:Tue(sday)?)&#34;</span> <span style="color:#666">2</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day-of-week</span> <span style="color:#ba2121">&#34;(?i:Wed(nesday)?)&#34;</span> <span style="color:#666">3</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day-of-week</span> <span style="color:#ba2121">&#34;(?i:Thu(rsday)?)&#34;</span> <span style="color:#666">4</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day-of-week</span> <span style="color:#ba2121">&#34;(?i:Fri(day)?)&#34;</span> <span style="color:#666">5</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day-of-week</span> <span style="color:#ba2121">&#34;(?i:Sat(urday)?)&#34;</span> <span style="color:#666">6</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">day-of-week</span> <span style="color:#ba2121">&#34;(?i:Sun(day)?)&#34;</span> <span style="color:#666">7</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Jan(uary)?)&#34;</span> <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Feb(ruary)?)&#34;</span> <span style="color:#666">2</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Mar(ch)?)&#34;</span> <span style="color:#666">3</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Apr(il)?)&#34;</span> <span style="color:#666">4</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:May)&#34;</span> <span style="color:#666">5</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Jun(e)?)&#34;</span> <span style="color:#666">6</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Jul(y)?)&#34;</span> <span style="color:#666">7</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Aug(ust)?)&#34;</span> <span style="color:#666">8</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Sep(tember)?)&#34;</span> <span style="color:#666">9</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Oct(ober)?)&#34;</span> <span style="color:#666">10</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Nov(ember)?)&#34;</span> <span style="color:#666">11</span>)
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">month</span> <span style="color:#ba2121">&#34;(?i:Dec(ember)?)&#34;</span> <span style="color:#666">12</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:[0-9]+[/\\ \-][0-9]+[/\\ \-][0-9]+)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#008000;font-weight:bold">let*</span> ((<span style="color:#19177c">date-list</span> (<span style="color:#19177c">split</span> <span style="color:#ba2121">&#34;[/\\ \-]&#34;</span> <span style="color:#ba2121">&#34;08/09/2008&#34;</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">day</span> (<span style="color:#00f">parse-integer</span> (<span style="color:#00f">car</span> <span style="color:#19177c">date-list</span>)))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">month</span> (<span style="color:#00f">parse-integer</span> (<span style="color:#00f">cadr</span> <span style="color:#19177c">date-list</span>)))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">year</span> (<span style="color:#00f">parse-integer</span> (<span style="color:#00f">caddr</span> <span style="color:#19177c">date-list</span>))))
</span></span><span style="display:flex;"><span>       (<span style="color:#19177c">make-date</span> <span style="color:#19177c">day</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:Now)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#00f">get-universal-time</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:Today)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#00f">get-universal-time</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date-range</span> <span style="color:#ba2121">&#34;(?i:This week)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day-of-week</span> <span style="color:#19177c">day-of-week</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#00f;font-weight:bold">cons</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) (<span style="color:#00f">-</span> <span style="color:#19177c">day-of-week</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) (<span style="color:#00f">-</span> <span style="color:#666">6</span> <span style="color:#19177c">day-of-week</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date-range</span> <span style="color:#ba2121">&#34;(?i:Last week)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day-of-week</span> <span style="color:#19177c">day-of-week</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#00f;font-weight:bold">cons</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) (<span style="color:#00f">-</span> (<span style="color:#00f">+</span> <span style="color:#19177c">day-of-week</span> <span style="color:#666">7</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) (<span style="color:#00f">-</span> (<span style="color:#00f">-</span> <span style="color:#19177c">day-of-week</span>) <span style="color:#666">1</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date-range</span> <span style="color:#ba2121">&#34;(?i:Next week)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day-of-week</span> <span style="color:#19177c">day-of-week</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#00f;font-weight:bold">cons</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) (<span style="color:#00f">-</span> <span style="color:#666">7</span> <span style="color:#19177c">day-of-week</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) (<span style="color:#00f">+</span> <span style="color:#666">6</span> (<span style="color:#00f">-</span> <span style="color:#666">7</span> <span style="color:#19177c">day-of-week</span>))))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date-range</span> <span style="color:#ba2121">&#34;(?i:This month)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#00f;font-weight:bold">cons</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">make-date</span> <span style="color:#666">1</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>) (<span style="color:#19177c">make-date</span> (<span style="color:#19177c">days-in-month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date-range</span> <span style="color:#ba2121">&#34;(?i:Next month)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">next-month</span> (<span style="color:#00f">+</span> <span style="color:#19177c">month</span> <span style="color:#666">1</span>)) (<span style="color:#19177c">next-year</span> <span style="color:#19177c">year</span>))
</span></span><span style="display:flex;"><span>         (<span style="color:#008000">when</span> (<span style="color:#00f">&gt;</span> <span style="color:#19177c">next-month</span> <span style="color:#666">12</span>)
</span></span><span style="display:flex;"><span>           (<span style="color:#008000">setf</span> <span style="color:#19177c">next-month</span> <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>           (<span style="color:#008000">setf</span> <span style="color:#19177c">next-year</span> (<span style="color:#00f">+</span> <span style="color:#19177c">year</span> <span style="color:#666">1</span>)))
</span></span><span style="display:flex;"><span>         (<span style="color:#00f;font-weight:bold">cons</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#19177c">make-date</span> <span style="color:#666">1</span> <span style="color:#19177c">next-month</span> <span style="color:#19177c">next-year</span>) (<span style="color:#19177c">make-date</span> (<span style="color:#19177c">days-in-month</span> <span style="color:#19177c">next-month</span> <span style="color:#19177c">next-year</span>) <span style="color:#19177c">next-month</span> <span style="color:#19177c">next-year</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date-range</span> <span style="color:#ba2121">&#34;(?i:Last month)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">last-month</span> (<span style="color:#00f">-</span> <span style="color:#19177c">month</span> <span style="color:#666">1</span>)) (<span style="color:#19177c">last-year</span> <span style="color:#19177c">year</span>))
</span></span><span style="display:flex;"><span>         (<span style="color:#008000">when</span> (<span style="color:#00f">&lt;</span> <span style="color:#19177c">last-month</span> <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>           (<span style="color:#008000">setf</span> <span style="color:#19177c">last-month</span> <span style="color:#666">12</span>)
</span></span><span style="display:flex;"><span>           (<span style="color:#008000">setf</span> <span style="color:#19177c">last-year</span> (<span style="color:#00f">-</span> <span style="color:#19177c">year</span> <span style="color:#666">1</span>)))
</span></span><span style="display:flex;"><span>         (<span style="color:#00f;font-weight:bold">cons</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#19177c">make-date</span> <span style="color:#666">1</span> <span style="color:#19177c">last-month</span> <span style="color:#19177c">last-year</span>) (<span style="color:#19177c">make-date</span> (<span style="color:#19177c">days-in-month</span> <span style="color:#19177c">last-month</span> <span style="color:#19177c">last-year</span>) <span style="color:#19177c">last-month</span> <span style="color:#19177c">last-year</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:start of month)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">make-date</span> <span style="color:#666">0</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:end of month)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">make-date</span> (<span style="color:#19177c">days-in-month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:end of year)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">make-date</span> (<span style="color:#19177c">days-in-month</span> <span style="color:#666">12</span> <span style="color:#19177c">year</span>) <span style="color:#666">12</span> <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:start of year)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) (<span style="color:#00f">get-universal-time</span>)
</span></span><span style="display:flex;"><span>         (<span style="color:#19177c">make-date</span> <span style="color:#666">0</span> <span style="color:#666">12</span> <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:Tomorrow)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date</span> <span style="color:#ba2121">&#34;(?i:Yesterday)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) <span style="color:#666">-1</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">date-range</span> <span style="color:#ba2121">&#34;(?i:ddget)&#34;</span>
</span></span><span style="display:flex;"><span>     (<span style="color:#00f;font-weight:bold">cons</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#19177c">x-days</span> (<span style="color:#19177c">x-working-days</span> (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) <span style="color:#666">-1</span>) <span style="color:#666">2</span>) <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#19177c">x-working-days</span> (<span style="color:#19177c">x-days</span> (<span style="color:#00f">get-universal-time</span>) <span style="color:#666">-1</span>) <span style="color:#666">3</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#19177c">year</span> <span style="color:#ba2121">&#34;[0-9]{4}&#34;</span> (<span style="color:#00f">parse-integer</span> <span style="color:#19177c">year</span> <span style="color:#19177c">:junk-allowed</span> <span style="color:#800">t</span>))
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">make-rule</span> <span style="color:#00f;font-weight:bold">number</span> <span style="color:#ba2121">&#34;[0-9]+&#34;</span> (<span style="color:#00f">parse-integer</span> <span style="color:#00f;font-weight:bold">number</span> <span style="color:#19177c">:junk-allowed</span> <span style="color:#800">t</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*date-logic-rules*</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#00f;font-weight:bold">list</span>
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">craft-rule</span> (<span style="color:#19177c">month</span> <span style="color:#19177c">m</span> <span style="color:#19177c">day</span> <span style="color:#19177c">d</span> <span style="color:#19177c">year</span> <span style="color:#19177c">y</span>) <span style="color:#19177c">date</span> (<span style="color:#19177c">make-date</span> <span style="color:#19177c">d</span> <span style="color:#19177c">m</span> <span style="color:#19177c">y</span>))
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">craft-rule</span> (<span style="color:#19177c">day</span> <span style="color:#19177c">d</span> <span style="color:#19177c">month</span> <span style="color:#19177c">m</span> <span style="color:#19177c">year</span> <span style="color:#19177c">y</span>) <span style="color:#19177c">date</span> (<span style="color:#19177c">make-date</span> <span style="color:#19177c">d</span> <span style="color:#19177c">m</span> <span style="color:#19177c">y</span>))
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">craft-rule</span> (<span style="color:#19177c">date</span> <span style="color:#19177c">start</span> <span style="color:#19177c">range-separator</span> <span style="color:#19177c">foo</span> <span style="color:#19177c">date</span> <span style="color:#19177c">end</span>) <span style="color:#19177c">date-range</span> (<span style="color:#19177c">make-date-range</span> <span style="color:#19177c">start</span> <span style="color:#19177c">end</span>))
</span></span><span style="display:flex;"><span>   (<span style="color:#19177c">craft-rule</span> (<span style="color:#19177c">date</span> <span style="color:#19177c">start</span> <span style="color:#19177c">date</span> <span style="color:#19177c">end</span>) <span style="color:#19177c">date-range</span> (<span style="color:#19177c">make-date-range</span> <span style="color:#19177c">start</span> <span style="color:#19177c">end</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">parse-date</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a string containing a date or range of dates and return some tokens&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">parse</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">*date-token-rules*</span> <span style="color:#19177c">*date-logic-rules*</span>))</span></span></code></pre></div>
<p>(Feel free to use it for whatever you want.)</p>
<p>One thing I&rsquo;m especially proud of is the way you make rules, you give the macro some regexp, a &ldquo;name&rdquo; symbol and some code to run and it returns to you a function which can then be called on a string to return any tokens found. It&rsquo;s nifty and lets you use pretty much the full power of the language inside each rule. Later on, I will be able to add rules so that it can handle stuff like &ldquo;A week ago last Thursday&rdquo; thanks to this power.</p>
<p>I&rsquo;m now trying to figure out what makes it run so slowly (it takes a few seconds to come back with tokens on a reasonably fast computer). I think it&rsquo;s to do with how I check for clashing tokens and remove them, but I&rsquo;m not sure exactly what the cause is. I have tried to use SBCL&rsquo;s profiling tools, but I have yet to make much sense of the output.</p>
<p>If anyone has any feedback on the code, or how I can make it run faster I&rsquo;d appreciate it.</p>
<p><em>Edit: Thanks to the guys in #lisp (especially H4ns and rsynott) I have now fixed the speed issue. CL-PPCRE works by creating a &lsquo;scanner&rsquo; function for each regexp which works really fast, but this takes a little time to create. The idea being you create all the scanners at compile-time and then use them at run-time. The problem was with the way my rule creating macro worked it didn&rsquo;t do this properly. Now the make-rule macro returns the code to create the scanner and then return the function using this scanner, and the problem is solved. A massive speed increase with one extra line of code, not bad going!</em></p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/08/25/visualising-working-days-with-vecto</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/08/25/visualising-working-days-with-vecto" />
    <title>Visualising Working days with Vecto</title>
    <published>2008-08-25T00:00:00+00:00</published>
    <updated>2008-08-25T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>While making the last Lisp program, I found it hard to visualise the working days between two dates. I ended up drawing these little sketches on a bit of paper to figure out if my program was doing the right thing or not!</p>
<p>Anyway, I decided that for my next project I would make a tool to display the dates for me. I am using <a href="http://www.xach.com/lisp/vecto/">Vecto library</a>, and so far I have found it to be excellent, straightforward to use but powerful at the same time.</p>
<p>The code is still rough and ready, but in a few hours I went from nothing to this:</p>
<p><img src="/images/blog/2workingdays.webp" alt="2 Working days visualised"></p>
<p>To generate that image I just ran this code: <code>(draw-date-range '(03 07 2008) '(7 07 2008) &quot;2workingdays.webp&quot;)</code></p>
<p>Next up I am going to tidy it all up, fix a few bugs and then get it working with a web interface through Hunchentoot so anyone with a web browser can make their own.</p>
<p>When the code&rsquo;s less thrown together, I&rsquo;ll share that too, Dave.</p>
<p>Edit:</p>
<p>I have been busy the last few evenings, and I have got the Hunchentoot interface working. &rsquo;ere&rsquo;s a couple of screenshots:</p>
<p><strong>before:</strong></p>
<p><img src="/images/blog/Working%20Days%20-%20before.webp" alt="Working Days - Before"></p>
<p><strong>and after:</strong></p>
<p><img src="/images/blog/Working%20Days%20-%20after.webp" alt="Working Days - After"></p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/08/21/generating-transaction-dates-in-lisp</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/08/21/generating-transaction-dates-in-lisp" />
    <title>Generating transaction dates in Lisp</title>
    <published>2008-08-21T00:00:00+00:00</published>
    <updated>2008-08-21T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>At work recently we found the need to check some dates in a database were correct. There&rsquo;s a database table that for any given date provides both the date two working days before and two working days after. The system uses it to figure out when it should send transactions so that they complete on the correct day.</p>
<p>Anyway, I have been trying to learn Lisp for a while so as a side project I decided to write something to generate a CSV of the required dates so that I could check everything lined up and so that we have the next few years worth of dates ready.</p>
<p>Here&rsquo;s the code, if you have any feedback I would greatly appreciate it as I am still really new to Common Lisp.</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-common-lisp" data-lang="common-lisp"><span style="display:flex;"><span>(<span style="color:#008000">defpackage</span> <span style="color:#19177c">:disco-date</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">:use</span> <span style="color:#19177c">:cl</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">:export</span> <span style="color:#19177c">:date-range</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:format-date</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:work-day?</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:next-day</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:prev-day</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:day-name</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:split-by-one-space</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:with-date</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:read-date</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:date-within-range?</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:date&gt;</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:date&gt;</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">:make-date</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">in-package</span> <span style="color:#19177c">#:disco-date</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*day-names*</span> <span style="color:#666">&#39;</span>((<span style="color:#666">01</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Mon&#34;</span>) (<span style="color:#666">02</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Tue&#34;</span>) (<span style="color:#666">03</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Wed&#34;</span>) (<span style="color:#666">04</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Thu&#34;</span>)
</span></span><span style="display:flex;"><span>                      (<span style="color:#666">05</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Fri&#34;</span>) (<span style="color:#666">06</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Sat&#34;</span>) (<span style="color:#666">0</span> <span style="color:#666">.</span> <span style="color:#ba2121">&#34;Sun&#34;</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*bank-holidays*</span> ())
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defvar</span> <span style="color:#19177c">*months*</span> <span style="color:#666">&#39;</span>((<span style="color:#666">01</span> <span style="color:#666">.</span> <span style="color:#666">31</span>) (<span style="color:#666">03</span> <span style="color:#666">.</span> <span style="color:#666">31</span>) (<span style="color:#666">04</span> <span style="color:#666">.</span> <span style="color:#666">30</span>) (<span style="color:#666">05</span> <span style="color:#666">.</span> <span style="color:#666">31</span>)
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">06</span> <span style="color:#666">.</span> <span style="color:#666">30</span>) (<span style="color:#666">07</span> <span style="color:#666">.</span> <span style="color:#666">31</span>) (<span style="color:#666">08</span> <span style="color:#666">.</span> <span style="color:#666">31</span>) (<span style="color:#666">09</span> <span style="color:#666">.</span> <span style="color:#666">30</span>)
</span></span><span style="display:flex;"><span>                  (<span style="color:#666">10</span> <span style="color:#666">.</span> <span style="color:#666">31</span>) (<span style="color:#666">11</span> <span style="color:#666">.</span> <span style="color:#666">30</span>) (<span style="color:#666">12</span> <span style="color:#666">.</span> <span style="color:#666">31</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defmacro</span> <span style="color:#19177c">with-date</span> ((<span style="color:#008000;font-weight:bold">&amp;key</span> (<span style="color:#19177c">day</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-day</span>) (<span style="color:#19177c">month</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-month</span>) (<span style="color:#19177c">year</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-year</span>)
</span></span><span style="display:flex;"><span>                           (<span style="color:#19177c">day-of-week</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-day-of-week</span>) (<span style="color:#00f">second</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-second</span>) (<span style="color:#19177c">minute</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-minute</span>)
</span></span><span style="display:flex;"><span>                           (<span style="color:#19177c">hour</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-hour</span>) (<span style="color:#19177c">tz</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-tz</span>) (<span style="color:#19177c">dst-p</span> <span style="color:#666">&#39;</span><span style="color:#19177c">#:unused-dst-p</span>))
</span></span><span style="display:flex;"><span>                     <span style="color:#19177c">date</span> <span style="color:#008000;font-weight:bold">&amp;body</span> <span style="color:#19177c">body</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a date, split it up into it&#39;s parts and run the code in body.&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">`</span>(<span style="color:#008000">multiple-value-bind</span>
</span></span><span style="display:flex;"><span>         (<span style="color:#666">,</span><span style="color:#00f">second</span> <span style="color:#666">,</span><span style="color:#19177c">minute</span> <span style="color:#666">,</span><span style="color:#19177c">hour</span> <span style="color:#666">,</span><span style="color:#19177c">day</span> <span style="color:#666">,</span><span style="color:#19177c">month</span> <span style="color:#666">,</span><span style="color:#19177c">year</span> <span style="color:#666">,</span><span style="color:#19177c">day-of-week</span> <span style="color:#666">,</span><span style="color:#19177c">dst-p</span> <span style="color:#666">,</span><span style="color:#19177c">tz</span>)
</span></span><span style="display:flex;"><span>       (<span style="color:#00f">decode-universal-time</span> <span style="color:#666">,</span><span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>     (<span style="color:#008000;font-weight:bold">declare</span> (<span style="color:#008000;font-weight:bold">ignorable</span> <span style="color:#666">,</span><span style="color:#00f">second</span> <span style="color:#666">,</span><span style="color:#19177c">minute</span> <span style="color:#666">,</span><span style="color:#19177c">hour</span> <span style="color:#666">,</span><span style="color:#19177c">day</span> <span style="color:#666">,</span><span style="color:#19177c">month</span> <span style="color:#666">,</span><span style="color:#19177c">year</span> <span style="color:#666">,</span><span style="color:#19177c">day-of-week</span> <span style="color:#666">,</span><span style="color:#19177c">dst-p</span> <span style="color:#666">,</span><span style="color:#19177c">tz</span>))
</span></span><span style="display:flex;"><span>     <span style="color:#666">,@</span><span style="color:#19177c">body</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">leap-year?</span> (<span style="color:#19177c">year</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a year and return T if a leap year, otherwise NIL&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">or</span> (<span style="color:#008000">and</span> (<span style="color:#00f">eql</span> <span style="color:#666">0</span> (<span style="color:#00f">mod</span> <span style="color:#19177c">year</span> <span style="color:#666">4</span>)) (<span style="color:#00f">not</span> (<span style="color:#00f">eql</span> <span style="color:#666">0</span> (<span style="color:#00f">mod</span> <span style="color:#19177c">year</span> <span style="color:#666">100</span>))))
</span></span><span style="display:flex;"><span>          (<span style="color:#00f">eql</span> <span style="color:#666">0</span> (<span style="color:#00f">mod</span> <span style="color:#19177c">year</span> <span style="color:#666">400</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">days-in-month</span> (<span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Takes a month,year and returns the number of days in it&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">not</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">month</span> <span style="color:#666">2</span>))
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">cdr</span> (<span style="color:#00f">assoc</span> <span style="color:#19177c">month</span> <span style="color:#19177c">*months*</span>))
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#19177c">leap-year?</span> <span style="color:#19177c">year</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#666">29</span>
</span></span><span style="display:flex;"><span>          <span style="color:#666">28</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">real-date?</span> (<span style="color:#19177c">day</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a day, month and year and return t if the date is valid, nil otherwise&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#00f">&gt;</span> <span style="color:#19177c">day</span> <span style="color:#666">0</span>) (<span style="color:#00f">&gt;</span> <span style="color:#19177c">month</span> <span style="color:#666">0</span>) (<span style="color:#00f">&gt;</span> <span style="color:#19177c">year</span> <span style="color:#666">1000</span>)
</span></span><span style="display:flex;"><span>           (<span style="color:#00f">&lt;=</span> <span style="color:#19177c">month</span> <span style="color:#666">12</span>)
</span></span><span style="display:flex;"><span>           (<span style="color:#00f">&lt;=</span> <span style="color:#19177c">year</span> <span style="color:#666">3000</span>)
</span></span><span style="display:flex;"><span>           (<span style="color:#00f">&lt;=</span> <span style="color:#19177c">day</span> (<span style="color:#19177c">days-in-month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>      <span style="color:#800">t</span>
</span></span><span style="display:flex;"><span>      <span style="color:#800">nil</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">make-date</span> (<span style="color:#19177c">day</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span> <span style="color:#008000;font-weight:bold">&amp;optional</span> (<span style="color:#19177c">hour</span> <span style="color:#666">0</span>) (<span style="color:#19177c">minute</span> <span style="color:#666">0</span>) (<span style="color:#00f">second</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take the different parts of a date and return a universal date&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">when</span> (<span style="color:#19177c">real-date?</span> <span style="color:#19177c">day</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">encode-universal-time</span> <span style="color:#00f">second</span> <span style="color:#19177c">minute</span> <span style="color:#19177c">hour</span> <span style="color:#19177c">day</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">gregorian-weekday</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Credit to someone in #emacs (consolers I think but I forget)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return the day of week for the date given, 0 indexed so 0 for Sunday, 1 for Monday etc&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">mod</span> (<span style="color:#00f">+</span> <span style="color:#666">0</span>
</span></span><span style="display:flex;"><span>            (<span style="color:#00f">*</span> <span style="color:#666">365</span> (<span style="color:#00f">1-</span> <span style="color:#19177c">year</span>)) (<span style="color:#00f">floor</span> (<span style="color:#00f">1-</span> <span style="color:#19177c">year</span>) <span style="color:#666">4</span>) (<span style="color:#00f">-</span> (<span style="color:#00f">floor</span> (<span style="color:#00f">1-</span> <span style="color:#19177c">year</span>) <span style="color:#666">100</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#00f">floor</span> (<span style="color:#00f">1-</span> <span style="color:#19177c">year</span>) <span style="color:#666">400</span>) (<span style="color:#00f">floor</span> (<span style="color:#00f">-</span> (<span style="color:#00f">*</span> <span style="color:#666">367</span> <span style="color:#19177c">month</span>) <span style="color:#666">362</span>) <span style="color:#666">12</span>)
</span></span><span style="display:flex;"><span>            (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">&lt;=</span> <span style="color:#19177c">month</span> <span style="color:#666">2</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#666">0</span>
</span></span><span style="display:flex;"><span>                (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#00f">=</span> (<span style="color:#00f">mod</span> <span style="color:#19177c">year</span> <span style="color:#666">4</span>) <span style="color:#666">0</span>) (<span style="color:#00f">not</span> (<span style="color:#00f">member</span> (<span style="color:#00f">mod</span> <span style="color:#19177c">year</span> <span style="color:#666">400</span>) (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#666">100</span> <span style="color:#666">200</span> <span style="color:#666">300</span>))))
</span></span><span style="display:flex;"><span>                    <span style="color:#666">-1</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#666">-2</span>))
</span></span><span style="display:flex;"><span>            <span style="color:#19177c">day</span>)
</span></span><span style="display:flex;"><span>         <span style="color:#666">7</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">file-lines</span> (<span style="color:#19177c">path</span>)
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Credit to http://www.cl-user.net/asp/html-docs/process-file-snippset</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Sucks up an entire file from PATH into a list of freshly-allocated
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">      strings, returning two values: the list of strings and the number of
</span></span></span><span style="display:flex;"><span><span style="color:#ba2121">      lines read.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">with-open-file</span> (<span style="color:#19177c">s</span> <span style="color:#19177c">path</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">line</span> <span style="color:#00f">=</span> (<span style="color:#00f">read-line</span> <span style="color:#19177c">s</span> <span style="color:#800">nil</span> <span style="color:#800">nil</span>)
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">while</span> <span style="color:#19177c">line</span>
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">collect</span> <span style="color:#19177c">line</span> <span style="color:#19177c">into</span> <span style="color:#19177c">lines</span>
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">counting</span> <span style="color:#800">t</span> <span style="color:#19177c">into</span> <span style="color:#19177c">line-count</span>
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">finally</span> (<span style="color:#008000">return</span> (<span style="color:#00f">values</span> <span style="color:#19177c">lines</span> <span style="color:#19177c">line-count</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">split-by-one-space</span> (<span style="color:#00f;font-weight:bold">string</span>)
</span></span><span style="display:flex;"><span><span style="color:#408080;font-style:italic">; Credit to http://cl-cookbook.sourceforge.net/strings.html#reverse</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Returns a list of substrings of string divided by ONE space each. Note: Two consecutive spaces will be seen as if there were an empty string between them.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">i</span> <span style="color:#00f">=</span> <span style="color:#666">0</span> <span style="color:#19177c">then</span> (<span style="color:#00f">1+</span> <span style="color:#19177c">j</span>)
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">as</span> <span style="color:#19177c">j</span> <span style="color:#00f">=</span> (<span style="color:#00f">position</span> <span style="color:#ba2121">#\Space</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">:start</span> <span style="color:#19177c">i</span>)
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">collect</span> (<span style="color:#00f">parse-integer</span> (<span style="color:#00f">subseq</span> <span style="color:#00f;font-weight:bold">string</span> <span style="color:#19177c">i</span> <span style="color:#19177c">j</span>) <span style="color:#19177c">:junk-allowed</span> <span style="color:#800">t</span>)
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">while</span> <span style="color:#19177c">j</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">read-date</span> (<span style="color:#19177c">date-string</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a string containing a date and return the date structure, or NIL if the string is in the wrong format&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">loaded-date</span> (<span style="color:#19177c">split-by-one-space</span> <span style="color:#19177c">date-string</span>)))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#00f">equal</span> (<span style="color:#00f">length</span> <span style="color:#19177c">loaded-date</span>) <span style="color:#666">3</span>)
</span></span><span style="display:flex;"><span>             (<span style="color:#00f">every</span> <span style="color:#19177c">&#39;numberp</span> <span style="color:#19177c">loaded-date</span>)
</span></span><span style="display:flex;"><span>             (<span style="color:#19177c">real-date?</span> (<span style="color:#00f">car</span> <span style="color:#19177c">loaded-date</span>) (<span style="color:#00f">cadr</span> <span style="color:#19177c">loaded-date</span>) (<span style="color:#00f">caddr</span> <span style="color:#19177c">loaded-date</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">make-date</span> (<span style="color:#00f">car</span> <span style="color:#19177c">loaded-date</span>) (<span style="color:#00f">cadr</span> <span style="color:#19177c">loaded-date</span>) (<span style="color:#00f">caddr</span> <span style="color:#19177c">loaded-date</span>))
</span></span><span style="display:flex;"><span>        <span style="color:#800">nil</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">load-date</span> (<span style="color:#19177c">filename</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a filename and read a list of space separated dates into a list, returns the list&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000">loop</span> <span style="color:#19177c">for</span> <span style="color:#19177c">line</span> <span style="color:#19177c">in</span> (<span style="color:#19177c">file-lines</span> <span style="color:#19177c">filename</span>)
</span></span><span style="display:flex;"><span>     <span style="color:#19177c">collect</span> (<span style="color:#19177c">split-by-one-space</span> <span style="color:#19177c">line</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">setf</span> <span style="color:#19177c">*bank-holidays*</span> (<span style="color:#19177c">load-date</span> <span style="color:#ba2121">&#34;/var/www/lisp/bank-holidays.txt&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">bank-holiday?</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return T if bank-holiday and nil otherwise&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">find</span> <span style="color:#19177c">date</span> <span style="color:#19177c">*bank-holidays*</span> <span style="color:#19177c">:test</span> <span style="color:#00f">#&#39;equal</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">next-day</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a date and return the next one&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">not</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">day</span> (<span style="color:#19177c">days-in-month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">make-date</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#19177c">day</span>) <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">month</span> <span style="color:#666">12</span>)
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">make-date</span> <span style="color:#666">1</span> <span style="color:#666">1</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#19177c">year</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">make-date</span> <span style="color:#666">1</span> (<span style="color:#00f">+</span> <span style="color:#666">1</span> <span style="color:#19177c">month</span>) <span style="color:#19177c">year</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">prev-day</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a date and return the previous one&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">not</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">day</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#19177c">make-date</span> (<span style="color:#00f">-</span> <span style="color:#19177c">day</span> <span style="color:#666">1</span>) <span style="color:#19177c">month</span> <span style="color:#19177c">year</span>)
</span></span><span style="display:flex;"><span>        (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">eql</span> <span style="color:#19177c">month</span> <span style="color:#666">1</span>)
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">make-date</span> (<span style="color:#19177c">days-in-month</span> <span style="color:#666">12</span> <span style="color:#19177c">year</span>) <span style="color:#666">12</span> (<span style="color:#00f">-</span> <span style="color:#19177c">year</span> <span style="color:#666">1</span>))
</span></span><span style="display:flex;"><span>            (<span style="color:#19177c">make-date</span> (<span style="color:#19177c">days-in-month</span> (<span style="color:#00f">-</span> <span style="color:#19177c">month</span> <span style="color:#666">1</span>) <span style="color:#19177c">year</span>) (<span style="color:#00f">-</span> <span style="color:#19177c">month</span> <span style="color:#666">1</span>) <span style="color:#19177c">year</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">weekday?</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return the day number if a weekday, otherwise NIL&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">day-of-week</span> (<span style="color:#19177c">gregorian-weekday</span> <span style="color:#19177c">date</span>)))
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#00f">&lt;</span> <span style="color:#19177c">day-of-week</span> <span style="color:#666">6</span>) (<span style="color:#00f">&gt;</span> <span style="color:#19177c">day-of-week</span> <span style="color:#666">0</span>))
</span></span><span style="display:flex;"><span>          <span style="color:#19177c">day-of-week</span>
</span></span><span style="display:flex;"><span>          <span style="color:#800">NIL</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">work-day?</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a Date and return t if it&#39;s a working day, NIL otherwise&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#19177c">weekday?</span> <span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>             (<span style="color:#00f">not</span> (<span style="color:#19177c">bank-holiday?</span> <span style="color:#19177c">date</span>)))
</span></span><span style="display:flex;"><span>        <span style="color:#800">T</span>
</span></span><span style="display:flex;"><span>        <span style="color:#800">nil</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">x-working-days</span> (<span style="color:#19177c">date</span> <span style="color:#19177c">x</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a date and return the date of x working days afterwards (negative number for previous)&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">eql</span> <span style="color:#666">0</span> <span style="color:#19177c">x</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">current-date</span> <span style="color:#19177c">date</span>) (<span style="color:#19177c">i</span> <span style="color:#666">-1</span>))
</span></span><span style="display:flex;"><span>        (<span style="color:#008000">loop</span>
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">until</span> (<span style="color:#00f">&gt;</span> <span style="color:#19177c">i</span> (<span style="color:#00f">abs</span> <span style="color:#19177c">x</span>))
</span></span><span style="display:flex;"><span>           <span style="color:#008000">when</span> (<span style="color:#19177c">work-day?</span> <span style="color:#19177c">current-date</span>)
</span></span><span style="display:flex;"><span>           <span style="color:#008000">do</span> (<span style="color:#008000">incf</span> <span style="color:#19177c">i</span>)
</span></span><span style="display:flex;"><span>           <span style="color:#008000">when</span> (<span style="color:#00f">&gt;</span> (<span style="color:#00f">abs</span> <span style="color:#19177c">x</span>) <span style="color:#19177c">i</span>)
</span></span><span style="display:flex;"><span>           <span style="color:#008000">do</span> (<span style="color:#008000">setf</span> <span style="color:#19177c">current-date</span>
</span></span><span style="display:flex;"><span>                    (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">&gt;</span> <span style="color:#19177c">x</span> <span style="color:#666">0</span>)
</span></span><span style="display:flex;"><span>                        (<span style="color:#19177c">next-day</span> <span style="color:#19177c">current-date</span>)
</span></span><span style="display:flex;"><span>                        (<span style="color:#19177c">prev-day</span> <span style="color:#19177c">current-date</span>)))
</span></span><span style="display:flex;"><span>           <span style="color:#19177c">finally</span> (<span style="color:#008000">return</span> <span style="color:#19177c">current-date</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">return-dates</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a &#39;valueDate&#39; and return the send and completion date&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#19177c">work-day?</span> <span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>              (<span style="color:#19177c">x-working-days</span> <span style="color:#19177c">date</span> <span style="color:#666">-2</span>)
</span></span><span style="display:flex;"><span>              (<span style="color:#19177c">x-working-days</span> <span style="color:#19177c">date</span> <span style="color:#666">-1</span>))
</span></span><span style="display:flex;"><span>          (<span style="color:#19177c">x-working-days</span> <span style="color:#19177c">date</span> <span style="color:#666">2</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">grab-dates</span> (<span style="color:#19177c">start-date</span> <span style="color:#19177c">end-date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Get the send and completion date for every date in the range (inclusive), then return a list of &#39;um&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">let</span> ((<span style="color:#19177c">current-date</span> <span style="color:#19177c">start-date</span>))
</span></span><span style="display:flex;"><span>    (<span style="color:#008000">loop</span> <span style="color:#19177c">until</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">current-date</span> (<span style="color:#19177c">next-day</span> <span style="color:#19177c">end-date</span>))
</span></span><span style="display:flex;"><span>       <span style="color:#19177c">collect</span> (<span style="color:#19177c">return-dates</span> <span style="color:#19177c">current-date</span>)
</span></span><span style="display:flex;"><span>       <span style="color:#008000">do</span> (<span style="color:#008000">setf</span> <span style="color:#19177c">current-date</span> (<span style="color:#19177c">next-day</span> <span style="color:#19177c">current-date</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">format-number</span> (<span style="color:#00f;font-weight:bold">number</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a number and return it as a string, put a 0 in front of it if it&#39;s single digits&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#00f">&gt;</span> <span style="color:#00f;font-weight:bold">number</span> <span style="color:#666">-1</span>) (<span style="color:#00f">&lt;</span> <span style="color:#00f;font-weight:bold">number</span> <span style="color:#666">10</span>))
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;0~A&#34;</span> <span style="color:#00f;font-weight:bold">number</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;~A&#34;</span> <span style="color:#00f;font-weight:bold">number</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">format-date</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take a date and return it as a nicely formatted string&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">format</span> <span style="color:#800">nil</span> <span style="color:#ba2121">&#34;~A/~A/~A&#34;</span> (<span style="color:#19177c">format-number</span> <span style="color:#19177c">day</span>) (<span style="color:#19177c">format-number</span> <span style="color:#19177c">month</span>) <span style="color:#19177c">year</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">day-name</span> (<span style="color:#19177c">date</span>)
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">year</span>) <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#00f">cdr</span> (<span style="color:#00f">assoc</span> (<span style="color:#19177c">gregorian-weekday</span> <span style="color:#19177c">date</span>) <span style="color:#19177c">*day-names*</span>))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">date&gt;</span> (<span style="color:#00f">first</span> <span style="color:#00f">second</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Returns nil if second date is larger than first, otherwise it returns first&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">first-day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">first-month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">first-year</span>) <span style="color:#00f">first</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#19177c">with-date</span> (<span style="color:#19177c">:day</span> <span style="color:#19177c">second-day</span> <span style="color:#19177c">:month</span> <span style="color:#19177c">second-month</span> <span style="color:#19177c">:year</span> <span style="color:#19177c">second-year</span>) <span style="color:#00f">second</span>
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">equal</span> (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">first-day</span> <span style="color:#19177c">first-month</span> <span style="color:#19177c">first-year</span>) (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">second-day</span> <span style="color:#19177c">second-month</span> <span style="color:#19177c">second-year</span>))
</span></span><span style="display:flex;"><span>          <span style="color:#800">nil</span>
</span></span><span style="display:flex;"><span>          (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">or</span> (<span style="color:#00f">&gt;</span> <span style="color:#19177c">first-year</span> <span style="color:#19177c">second-year</span>)
</span></span><span style="display:flex;"><span>                  (<span style="color:#008000">and</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">first-year</span> <span style="color:#19177c">second-year</span>)
</span></span><span style="display:flex;"><span>                       (<span style="color:#00f">&gt;</span> <span style="color:#19177c">first-month</span> <span style="color:#19177c">second-month</span>))
</span></span><span style="display:flex;"><span>                  (<span style="color:#008000">and</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">first-year</span> <span style="color:#19177c">second-year</span>)
</span></span><span style="display:flex;"><span>                       (<span style="color:#00f">equal</span> <span style="color:#19177c">first-month</span> <span style="color:#19177c">second-month</span>)
</span></span><span style="display:flex;"><span>                       (<span style="color:#00f">&gt;</span> <span style="color:#19177c">first-day</span> <span style="color:#19177c">second-day</span>)))
</span></span><span style="display:flex;"><span>              <span style="color:#00f">first</span>
</span></span><span style="display:flex;"><span>              <span style="color:#800">nil</span>)))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">date&lt;</span> (<span style="color:#00f">first</span> <span style="color:#00f">second</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Returns nil if second date is smaller than first, otherwise it returns first&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#19177c">date&gt;</span> <span style="color:#00f">second</span> <span style="color:#00f">first</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#00f">first</span>
</span></span><span style="display:flex;"><span>      <span style="color:#800">nil</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">date-within-range?</span> (<span style="color:#19177c">date</span> <span style="color:#19177c">max-date</span> <span style="color:#19177c">min-date</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Return date if it&#39;s in-between the min and max dates, inclusively&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#008000">and</span> (<span style="color:#008000">or</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">date</span> <span style="color:#19177c">min-date</span>) (<span style="color:#19177c">date&gt;</span> <span style="color:#19177c">date</span> <span style="color:#19177c">min-date</span>))
</span></span><span style="display:flex;"><span>           (<span style="color:#008000">or</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">date</span> <span style="color:#19177c">max-date</span>) (<span style="color:#19177c">date&lt;</span> <span style="color:#19177c">date</span> <span style="color:#19177c">max-date</span>)))
</span></span><span style="display:flex;"><span>      <span style="color:#19177c">date</span>
</span></span><span style="display:flex;"><span>      <span style="color:#800">nil</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>(<span style="color:#008000">defun</span> <span style="color:#19177c">date-range</span> (<span style="color:#19177c">start</span> <span style="color:#19177c">end</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#ba2121">&#34;Take two dates and return the inclusive range between them.&#34;</span>
</span></span><span style="display:flex;"><span>  (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#00f">equal</span> <span style="color:#19177c">start</span> <span style="color:#19177c">end</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#00f;font-weight:bold">list</span> <span style="color:#19177c">start</span>)
</span></span><span style="display:flex;"><span>      (<span style="color:#008000;font-weight:bold">if</span> (<span style="color:#19177c">date&gt;</span> <span style="color:#19177c">start</span> <span style="color:#19177c">end</span>)
</span></span><span style="display:flex;"><span>          (<span style="color:#00f;font-weight:bold">cons</span> <span style="color:#19177c">start</span> (<span style="color:#19177c">date-range</span> (<span style="color:#19177c">prev-day</span> <span style="color:#19177c">start</span>) <span style="color:#19177c">end</span>))
</span></span><span style="display:flex;"><span>          (<span style="color:#00f;font-weight:bold">cons</span> <span style="color:#19177c">start</span> (<span style="color:#19177c">date-range</span> (<span style="color:#19177c">next-day</span> <span style="color:#19177c">start</span>) <span style="color:#19177c">end</span>)))))</span></span></code></pre></div>
<p>Here is the <a href="/files/blog/bank-holidays1.txt">bank-holidays1.txt</a> I used, it contains all the UK bank holidays for the next few years.</p>
<p>Feel free to use the bits I wrote for anything you want, any feedback of how I could improve things would be appreciated.</p>
<p>Cheers, Dave.</p>
<p><strong>Edit:</strong></p>
<p>I have taken some of the advice given in that comment, and I have tweaked the code. I noticed that transactions take three days to complete, inclusive of the send day. So while allowing two working days is usually enough, we need to allow three if we&rsquo;re sending the transaction on a weekend or bank holiday. I also fixed a bug to get rid of a trailing space on all lines when printing the CSV.</p>
<p>Interestingly I did find <a href="/images/blog/date-mistake.webp">some</a> <a href="/images/blog/date-mistake2.webp">mistakes</a> after comparing my results with a dump of the database&rsquo;s table, so it was worth checking after all!</p>
<p>Next up I am going to take up the suggestion of using <code>decode-universal-time</code> and <code>encode-universal-time</code>. (Hopefully, that will mainly involve changing the <code>with-date</code> macro, we will see&hellip;)</p>
<p><strong>Edit2:</strong></p>
<p>I have sorted out the <code>with-date</code> macro and other parts of the code, so they now use proper universal time-stamps.</p>
<p>Also, I have been forced to learn how packages work to get Hunchentoot and co to work so I have now made this into a package that I can use from my other projects.</p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2008/06/14/sql-injection-comment</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2008/06/14/sql-injection-comment" />
    <title>SQL Injection comment</title>
    <published>2008-06-14T00:00:00+00:00</published>
    <updated>2008-06-14T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Someone posted a comment to this blog earlier, looks like they were trying to use SQL injection to beat the spam filters or it may have been a real person trying their luck.</p>
<p>Anyway I find this stuff pretty interesting so I thought I would post the attack, here is what he posted:</p>
<div class="highlight"><pre tabindex="0" style=";-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>Bill883205666<span style="color:#ba2121">&#39;,&#39;</span><span style="color:#666">928884583</span>billy<span style="color:#666">@</span>msn.com<span style="color:#ba2121">&#39;,&#39;&#39;,&#39;</span><span style="color:#666">10</span>.<span style="color:#666">134</span>.<span style="color:#666">123</span>.<span style="color:#666">1</span><span style="color:#ba2121">&#39;,&#39;</span><span style="color:#666">2008</span><span style="color:#666">-</span><span style="color:#666">06</span><span style="color:#666">-</span><span style="color:#666">14</span><span style="color:#bbb"> </span><span style="color:#666">08</span>:<span style="color:#666">04</span>:<span style="color:#666">27</span><span style="color:#ba2121">&#39;,&#39;</span><span style="color:#666">2008</span><span style="color:#666">-</span><span style="color:#666">06</span><span style="color:#666">-</span><span style="color:#666">14</span><span style="color:#bbb"> </span><span style="color:#666">08</span>:<span style="color:#666">04</span>:<span style="color:#666">27</span><span style="color:#ba2121">&#39;,&#39;&#39;,&#39;</span><span style="color:#666">0</span><span style="color:#ba2121">&#39;,&#39;</span>lynx<span style="color:#ba2121">&#39;,&#39;</span><span style="color:#008000;font-weight:bold">comment</span><span style="color:#ba2121">&#39;,&#39;</span><span style="color:#666">0</span><span style="color:#ba2121">&#39;,&#39;</span><span style="color:#666">0</span><span style="color:#ba2121">&#39;),(&#39;</span><span style="color:#666">0</span><span style="color:#ba2121">&#39;, &#39;&#39;, &#39;&#39;, &#39;&#39;, &#39;&#39;, &#39;</span><span style="color:#666">008</span><span style="color:#666">-</span><span style="color:#666">06</span><span style="color:#666">-</span><span style="color:#666">15</span><span style="color:#bbb"> </span><span style="color:#666">08</span>:<span style="color:#666">04</span>:<span style="color:#666">27</span><span style="color:#ba2121">&#39;, &#39;</span><span style="color:#666">2008</span><span style="color:#666">-</span><span style="color:#666">06</span><span style="color:#666">-</span><span style="color:#666">15</span><span style="color:#bbb"> </span><span style="color:#666">08</span>:<span style="color:#666">04</span>:<span style="color:#666">27</span><span style="color:#ba2121">&#39;, &#39;&#39;, &#39;</span>spam<span style="color:#ba2121">&#39;, &#39;&#39;, &#39;</span><span style="color:#008000;font-weight:bold">comment</span><span style="color:#ba2121">&#39;, &#39;</span><span style="color:#666">0</span><span style="color:#ba2121">&#39;,&#39;</span><span style="color:#666">0</span><span style="color:#ba2121">&#39; ) /* | None | IP: 124.217.227.127</span></span></span></code></pre></div>
<p>I think it&rsquo;s pretty clever how he tries to make the SQL post his message with a fake IP address. I&rsquo;m also really curious why he takes the trouble to add a fake spam comment to the end of the query.</p>
<p>I looked up the IP address it seems to be associated with a shared server provided by svservers.com. I guess this guy didn&rsquo;t get a private IP address.</p>
<pre tabindex="0"><code>Results
124.217.227.127 resolves to &#34;svservers.com&#34;
Top Level Domain: &#34;svservers.com&#34;
Country IP Address: MALAYSIA
</code></pre><p>Anyway that&rsquo;s all for now. If anyone could explain more about what he&rsquo;s up to I would be interested to hear it.</p>
<hr>
<p><em>Edit: My blog used to run WordPress, so this post made a lot more sense at the time. I now use <a href="http://couchdb.apache.org/">CouchDB</a> and <a href="http://disqus.com/">comments are powered by Disqus</a>.</em></p>
<hr>
<p><em>Edit2: I no longer use a database at all for the blog! Unfortunately, in the transition, the quotes from the SQL have been munged somewhat. Hopefully, I&rsquo;ve got them right&hellip;</em></p>%!s(MISSING)]]></content>
  </entry>
  
  <entry>
    <id>https://kzar.co.uk/blog/2007/10/14/myspace-account-recovery</id>
    <link type="text/html" rel="alternate" href="https://kzar.co.uk/blog/2007/10/14/myspace-account-recovery" />
    <title>Myspace account recovery</title>
    <published>2007-10-14T00:00:00+00:00</published>
    <updated>2007-10-14T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>I thought I would start off the blog with something hackery&hellip; The other day my friend asked me to help get back into her Myspace account. She had forgotten the old email address she used to sign up to the site.</p>
<img src="/images/blog/ninja.webp" alt="Ninja" style="float: left;" />
My mission was to figure out the email and password used, login and then update her email and password to the new ones she can remember. All I had to go on was the rough structure of the email address. She only remembered it was a hotmail.co.uk address and the some of the words used.
<p>It was something like two words and a number, but she couldn&rsquo;t remember the exact order&hellip; e.g. <code>herbert1wompom@hotmail.co.uk</code> or <code>herbertwompom@hotmail.co.uk</code> or <code>herbertwompom1@hotmail.co.uk</code> or something similar. There where a few words and numbers she thought it probably was but she couldn&rsquo;t remember the exact combination.</p>
<p>Anyway, I figured the first step was to figure out what the exact email address was. I used the Myspace advanced search facility and started guessing combinations of the email address until I found a match. Eventually after 20 mins of guessing one of the email addresses linked to her profile, so I was halfway there.</p>
<p>Next, I had to compromise the email account, so I started trying to login to her Hotmail account with some of the passwords she thought it might be. After a while, I realised that the email account didn&rsquo;t exist (it must have expired) so I signed up using the appropriate account name to gain access to emails sent to that address.</p>
<p>Lastly, all I had to do was go on Myspace and click the &ldquo;I forgot my password&rdquo; button and I had everything I needed. I logged into her Myspace profile and was able to update the email address and password.</p>
<p>The main thing to remember from this is how powerful the Myspace search is. If I get an email from someone, I can instantly search Myspace for a profile with that email address and find out a lot of personal information. Scary!</p>%!s(MISSING)]]></content>
  </entry>
  
</feed>
