parent
72c1b072cc
commit
39791363fa
4 changed files with 490 additions and 2 deletions
@ -0,0 +1,288 @@ |
||||
--- |
||||
title: "Advent of Code: Day 1" |
||||
tags: |
||||
- Advent of Code |
||||
description: Day one of the Advent of Code |
||||
date: 2021-12-01 |
||||
--- |
||||
|
||||
So, it's December all ready. I haven't written anywhere near as many blogs this |
||||
year as I would like to. Hopefully the [Advent of |
||||
Code](https://adventofcode.com/2021/) this year will give me some things to |
||||
write about and make this years list of blogs look better. |
||||
|
||||
This is the first year I am doing the Advent of Code so don't really know what |
||||
to expect. However, with day 1 done, I think I'll probably enjoy it. |
||||
|
||||
If you're reading this, it is no longer day 1. I will refrain from publishing |
||||
the solutions until at least the day after they go live. However, if previous |
||||
years are anything to go by, you should still be able to view the puzzles and |
||||
have a go for yourself: |
||||
|
||||
<https://adventofcode.com/2021/day/1> |
||||
|
||||
I am going to use the project puzzles this year to try and learn C. I have used |
||||
it a little bit but certainly wouldn't call myself proficient. This is supposed |
||||
to be a way for me to get better so if you're looking for well formed C code by |
||||
someone who knows what they are doing, you should probably look elsewhere. |
||||
|
||||
My solutions are available here: |
||||
|
||||
<https://git.jonathanh.co.uk/jab2870/Advent-of-Code/src/branch/master/day1> |
||||
|
||||
## Part 1 |
||||
|
||||
The first part of today's challenge involved taking a list of numbers and |
||||
counting the number of times a number is higher than the previous number. |
||||
|
||||
The example they gave is with this list of numbers: |
||||
|
||||
``` |
||||
199 |
||||
200 |
||||
208 |
||||
210 |
||||
200 |
||||
207 |
||||
240 |
||||
269 |
||||
260 |
||||
263 |
||||
``` |
||||
|
||||
I put those in a text file called example.txt. |
||||
|
||||
### Bash |
||||
|
||||
I started by implementing it in bash. Why? Because I know bash and wanted to |
||||
make sure I had understood the problem before trying to battle with C. |
||||
|
||||
```bash |
||||
#!/usr/bin/env bash |
||||
|
||||
file="$1" |
||||
increasing=0 |
||||
last=99999999 |
||||
|
||||
while read line; do |
||||
if [ "$line" -gt "$last" ]; then |
||||
increasing="$(( increasing + 1 ))" |
||||
fi |
||||
last="$line" |
||||
done < <(cat "$file") |
||||
|
||||
echo "$increasing were increasing" |
||||
``` |
||||
|
||||
There is not a huge amount going on here. If you are at all familiar with bash, |
||||
you can probably see what is going on here. I initially set up variables to hold |
||||
the file name (passed in as the first argument), a counter for the number of |
||||
times we increase from one line to the next and the value of the last line we've |
||||
seen that I initially set to a large number. |
||||
|
||||
Then we enter the loop. It is worth noting that the loop had to be of the form: |
||||
|
||||
```bash |
||||
while read line; do |
||||
# Do stuff |
||||
done < <(cat "$file") |
||||
``` |
||||
|
||||
Rather than: |
||||
|
||||
```bash |
||||
cat "$file" | while read line; do |
||||
# Do stuff |
||||
done |
||||
``` |
||||
|
||||
The reason for this is that pipes are executed in a sub shell meaning they can't |
||||
change variables in the initial shell. In other words, with the second method, |
||||
we wouldn't be able to update the increasing and last variables from inside the |
||||
loop. |
||||
|
||||
Inside the loop, I check to see if the current value is larger than the previous |
||||
value and if it is I increment the `increacing` variable by 1. I then set the |
||||
`last` variable so it can be used for the next iteration. |
||||
|
||||
Finally, once all the lines have been done, I print out the number of lines that |
||||
were increasing. |
||||
|
||||
|
||||
### C |
||||
|
||||
So, in a surprise to nobody, my C implementation is not as short as the bash |
||||
one. However, it wasn't as bad as I thought it would be. |
||||
|
||||
Step 1 was trying to work out how to read a file in to c one file at a time. |
||||
|
||||
Given that I knew the format of the lines, I was able to use fscanf to get each |
||||
of them. |
||||
|
||||
Although the syntax is slightly different, the logic is very similar between the |
||||
Bash and C implementations. |
||||
|
||||
```c |
||||
#include <stdio.h> |
||||
|
||||
int main( int argc, char *argv[] ){ |
||||
if( argc == 2 ) { |
||||
unsigned int last = ~0; |
||||
unsigned int increasing = 0; |
||||
unsigned int value; |
||||
|
||||
// Read the values into an array |
||||
FILE *fp=fopen(argv[1], "r"); |
||||
while (!feof (fp)){ |
||||
fscanf(fp, "%d\n", &value); |
||||
if ( value > last ) |
||||
increasing++; |
||||
last = value; |
||||
} |
||||
fclose(fp); |
||||
|
||||
|
||||
printf( "%d were increasing\n", increasing ); |
||||
|
||||
return 0; |
||||
} else { |
||||
printf("You need to provide a file\n"); |
||||
return 1; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
## Part 2 |
||||
|
||||
The extension for part 2 was to change the program so that rather than comparing |
||||
lines with the previous line, the sum of 3 consecutive lines should be compared. |
||||
|
||||
``` |
||||
199 A |
||||
200 A B |
||||
208 A B C |
||||
210 B C D |
||||
200 E C D |
||||
207 E F D |
||||
240 E F G |
||||
269 F G H |
||||
260 G H |
||||
263 H |
||||
``` |
||||
|
||||
So, the sum of the numbers marked with A would be compared to the sum of those |
||||
marked with B. Then B with C and so on. |
||||
|
||||
I think the intended way of doing this is to first create an array type |
||||
structure of sums then use the code from part 1 to work out how many of these |
||||
sums are increasing. However, a short cut can be taken. The sum of the next |
||||
group of numbers is always going to be the sum of the previous numbers plus the |
||||
next digit, minus the first. It follows therefore that we can compare the groups |
||||
by comparing the differences of every 3 numbers rather than adjacent numbers. |
||||
|
||||
So, for example |
||||
|
||||
$$ A = 199 + 200 + 208 $$ |
||||
|
||||
$$ B = 200 + 208 + 210 $$ |
||||
|
||||
We can confirm that group B increases from group A because 210 is greater than |
||||
199. |
||||
|
||||
In order to do this, I want to be able to easily reference individual entries by |
||||
their index so rather than doing the comparisons on the first pass through the |
||||
file, I first create an array. I can then compare an entry with the entry 3 |
||||
before it. |
||||
|
||||
### Bash |
||||
|
||||
```bash |
||||
#!/usr/bin/env bash |
||||
|
||||
file="$1" |
||||
increasing=0 |
||||
|
||||
readarray -t "arr" < "$file" |
||||
|
||||
length="${#arr[@]}" |
||||
|
||||
for i in $(seq 3 "$length"); do |
||||
curr="${arr[$i]}" |
||||
prev3="${arr[$((i - 3))]}" |
||||
|
||||
if [ -n "$curr" ] && [ "$curr" -gt "$prev3" ]; then |
||||
increasing="$((increasing + 1))" |
||||
fi |
||||
done |
||||
|
||||
echo "$increasing were increacing" |
||||
``` |
||||
|
||||
You'll see that bash has a nice built-in for turning a file into an array. |
||||
However, any bonus points it it might get for that, it looses by having the |
||||
worst syntax ever for getting the length of that array. Who thought that |
||||
`${#arr[@]}` would ever be a good idea‽ |
||||
|
||||
### C |
||||
|
||||
```c |
||||
#include <stdio.h> |
||||
|
||||
int countLines(char filename[]){ |
||||
FILE *fp; |
||||
fp=fopen(filename, "r"); |
||||
long int lines =0; |
||||
|
||||
if ( fp == NULL ) { |
||||
return -1; |
||||
} |
||||
|
||||
while (EOF != (fscanf(fp, "%*[^\n]"), fscanf(fp,"%*c"))) |
||||
++lines; |
||||
|
||||
fclose(fp); |
||||
return lines; |
||||
} |
||||
|
||||
int main( int argc, char *argv[] ){ |
||||
if( argc == 2 ) { |
||||
// 1 less because file ends with trailing new line |
||||
int lines=countLines(argv[1]); |
||||
int values[lines]; |
||||
int line=0; |
||||
FILE *fp=fopen(argv[1], "r"); |
||||
int increasing = 0; |
||||
|
||||
// Read the values into an array |
||||
while (!feof (fp)){ |
||||
fscanf(fp, "%d\n", &values[line]); |
||||
|
||||
if ( (line >= 3) && (values[line] > values[line-3]) ){ |
||||
|
||||
increasing++; |
||||
|
||||
} |
||||
|
||||
line++; |
||||
} |
||||
fclose(fp); // closing file |
||||
|
||||
printf( "Increasing: %d\n", increasing ); |
||||
|
||||
return 0; |
||||
} else { |
||||
printf("You need to provide a file\n"); |
||||
return 1; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
So this is obviously longer than the bash version but mainly because there isn't |
||||
a nice way of putting a text file into an array. I have to first iterate through |
||||
the file to determine how many lines there are, then allocate that much space for |
||||
the array, then re-run through the file to get the values. |
||||
|
||||
Initially, I then had a third loop where I iterated through the newly created |
||||
array to determine if there was an increase. However, I then realised I could do |
||||
that whilst populating the array. |
||||
|
@ -0,0 +1,200 @@ |
||||
--- |
||||
title: "Multipart Emails in Neomutt" |
||||
tags: |
||||
- Mutt |
||||
description: Mutt now supports multipart email. I guess it will be easy to set it up right? |
||||
date: 2022-05-27 |
||||
--- |
||||
|
||||
It recently came to my attention that mutt now supports sending multipart |
||||
emails. I thought that this would mean that in half an hour or so I would have |
||||
html emails working. Turns out, I was wrong. What instead happened was weeks of |
||||
trial and error and reading RFCs. |
||||
|
||||
I now have a system I am happy with. I write an email in markdown and mutt |
||||
(along with some surrounding scripts) will convert that markdown to html, attach |
||||
inline images and create a multipart email. |
||||
|
||||
## A note about HTML emails |
||||
|
||||
If you do need to send HTML emails, please spare a thought for your recipient; |
||||
it is not just "weird" terminal email client users that could suffer. According |
||||
to the [National Eye Institute](https://www.nei.nih.gov/learn-about-eye-health/eye-conditions-and-diseases/color-blindness), |
||||
one in twelve men are colour blind; so please don't use only colour to |
||||
distinguish items. Additionally, many people suffer from vision-loss blindness, |
||||
who are likely to use screen readers or braille displays. Complex layouts or |
||||
heavy use of images are going to give these people a poor experience. |
||||
|
||||
That being said, HTML emails can be used for aesthetic purposes, whilst still |
||||
providing a plain text version for those who want it. That is the method I |
||||
suggest adopting if you need html emails, and the method this blog post will |
||||
describe. |
||||
|
||||
## Multipart / Related emails |
||||
|
||||
To begin with, we need to understand a little bit about how emails are |
||||
structured. Below is an example tree structure of a standard email. |
||||
|
||||
``` |
||||
Multipart Related |
||||
├─>Multipart Alternative |
||||
│ ├─>Plain Text Email |
||||
│ └─>HTML Email |
||||
└─>Inline Image Attachment |
||||
Non-Inline Attachment |
||||
``` |
||||
|
||||
Starting at the lowest level, we see a plain text email and an HTML email. These |
||||
are both wrapped in a multipart **alternative** wrapper. This tells email |
||||
clients receiving the email that they are alternative versions of the same |
||||
document. The email client will normally choose which to display based on the |
||||
mime type and user preferences. |
||||
|
||||
The multipart alternative wrapper and an image attachment are then wrapped in a |
||||
multipart **related** wrapper. This tells the email client that the contents are |
||||
related to one another, but not different version of the same document. This is |
||||
where inline images are attached. |
||||
|
||||
Finally, there is another attachment that is outside of the multipart related |
||||
wrapper. This will show up as another attachment but cannot be displayed inline. |
||||
|
||||
## Neomutt Configuration |
||||
|
||||
The conversion from markdown to html will be handled by an external script. It |
||||
will create files and instruct mutt to attach them. |
||||
|
||||
We can start with the following: |
||||
|
||||
```vimrc |
||||
macro compose Y "<first-entry>\ |
||||
<pipe-entry>convert-multipart<enter>\ |
||||
<enter-command>source /tmp/neomutt-attach-macro<enter> |
||||
``` |
||||
|
||||
We specify a macro to run when `Y` is pushed. First, we select the first entry. |
||||
This is in case we have attached anything manually, the first entry should be |
||||
the markdown file. |
||||
|
||||
We then pipe the selected entry (the markdown file) to an external script, in |
||||
this case a bash script called `convert-multipart`. Finally we source a file |
||||
called `/tmp/neomutt-commands`. This will be populated by the script and will |
||||
allow us to group and attach files inside neomutt. |
||||
|
||||
## Converting to HTML |
||||
|
||||
Let's start with a simple pandoc conversion. |
||||
|
||||
|
||||
```bash |
||||
#!/usr/bin/env bash |
||||
|
||||
commandsFile="/tmp/neomutt-commands" |
||||
markdownFile="/tmp/neomutt-markdown" |
||||
htmlFile="/tmp/neomutt.html" |
||||
|
||||
cat - > "$markdownFile" |
||||
echo -n "push " > "$commandsFile" |
||||
|
||||
pandoc -f markdown -t html5 --standalone --template ~/.pandoc/templates/email.html "$markdownFile" > "$htmlFile" |
||||
|
||||
# Attach the html file |
||||
echo -n "<attach-file>\"$htmlFile\"<enter>" >> "$commandsFile" |
||||
|
||||
# Set it as inline |
||||
echo -n "<toggle-disposition>" >> "$commandsFile" |
||||
|
||||
# Tell neomutt to delete it after sending |
||||
echo -n "<toggle-unlink>" >> "$commandsFile" |
||||
|
||||
# Select both the html and markdown files |
||||
echo -n "<tag-entry><previous-entry><tag-entry>" >> "$commandsFile" |
||||
|
||||
# Group the selected messages as alternatives |
||||
echo -n "<group-alternatives>" >> "$commandsFile" |
||||
``` |
||||
|
||||
The above bash script will create an html file using pandoc, and create a file |
||||
of neomutt commands. This instructs neomutt to attach the html file, set its |
||||
disposition, and group the markdown and html files into a "multipart |
||||
alternatives" group. |
||||
|
||||
Neomutt's attachment view should look something like this. |
||||
|
||||
``` |
||||
I 1 <no description> [multipa/alternativ, 7bit, 0K] |
||||
- I 2 ├─>/tmp/neomutt-hostname-1000-89755-7 [text/plain, 7bit, us-ascii, 0.3K] |
||||
- I 3 └─>/tmp/neomutt.html [text/html, 7bit, us-ascii, 9.5K] |
||||
``` |
||||
|
||||
## Inline attachments |
||||
|
||||
The next part of the puzzle is inline attachments. These need to be attached and |
||||
then grouped within a multipart related group. |
||||
|
||||
To reference the file from within the html email, each inline image needs a |
||||
unique cid. I use md5 sums for this. They are not cryptographically secure, but |
||||
for the purposes of generating unique strings for images in an email, they are |
||||
fine. |
||||
|
||||
```bash |
||||
grep -Eo '!\[[^]]*\]\([^)]+' "$markdownFile" | cut -d '(' -f 2 | |
||||
grep -Ev '^(cid:|https?://)' | while read file; do |
||||
id="cid:$(md5sum "$file" | cut -d ' ' -f 1 )" |
||||
sed -i "s#$file#$id#g" "$markdownFile" |
||||
done |
||||
``` |
||||
|
||||
We loop through all the images in the markdown file, and replace the paths for |
||||
cids (assuming they are not already cids or remote images). |
||||
|
||||
As the markdown has changed, we need to attach the new one and detach the old. |
||||
|
||||
```bash |
||||
if [ "$(grep -Eo '!\[[^]]*\]\([^)]+' "$markdownFile" | grep '^cid:' | wc -l)" -gt 0 ]; then |
||||
echo -n "<attach-file>\"$markdownFile\"<enter><first-entry><detach-file>" >> "$commandsFile" |
||||
fi |
||||
``` |
||||
|
||||
To attach the images, we loop through the original file and add to the file |
||||
neomutt sources. Neomutt will be instructed to attach, set the disposition, set |
||||
the content ID and tag the image. |
||||
|
||||
```bash |
||||
grep -Eo '!\[[^]]*\]\([^)]+' "${markdownFile}.orig" | cut -d '(' -f 2 | |
||||
grep -Ev '^(cid:|https?://)' | while read file; do |
||||
id="$(md5sum "$file" | cut -d ' ' -f 1 )" |
||||
echo -n "<attach-file>\"$file\"<enter>" >> "$commandsFile" |
||||
echo -n "<toggle-disposition>" >> "$commandsFile" |
||||
echo -n "<edit-content-id>^u\"$id\"<enter>" >> "$commandsFile" |
||||
echo -n "<tag-entry>" >> "$commandsFile" |
||||
done |
||||
``` |
||||
|
||||
```bash |
||||
if [ "$(grep -Eo '!\[[^]]*\]\([^)]+' "$markdownFile" | grep '^cid:' | wc -l)" -gt 0 ]; then |
||||
echo -n "<first-entry><tag-entry><group-related>" >> "$commandsFile" |
||||
fi |
||||
``` |
||||
|
||||
Finally, if there were any images attached, we select the first entry (the |
||||
multipart alternative we've already created), tag it and mark everything tagged |
||||
as multipart related. |
||||
|
||||
|
||||
``` |
||||
I 1 <no description> [multipa/related, 7bit, 0K] |
||||
I 2 ├─><no description> [multipa/alternativ, 7bit, 0K] |
||||
- I 3 │ ├─>/tmp/neomutt-markdown [text/plain, 7bit, us-ascii, 0.3K] |
||||
- I 4 │ └─>/tmp/neomutt.html [text/html, 7bit, us-ascii, 9.5K] |
||||
I 5 └─>/tmp/2022-05-27T15-02-20Z.png [image/png, base64, 0.5K] |
||||
``` |
||||
|
||||
At this point, the user is free to attach additional, non inline documents as |
||||
normal. This email should be good for both text based and graphical email |
||||
clients. |
||||
|
||||
|
||||
For the full source changes, see [this commit](https://git.jonathanh.co.uk/jab2870/Dotfiles/commit/08af357f4445e40e98c715faab6bb3b075ec8afa). |
||||
|
||||
|
||||
|
Loading…
Reference in new issue