Telephone +44(0)1524 64544
Email: info@shadowcat.co.uk

Merry Slidesmas!

Sun Dec 23 04:30:00 2012

Merry Slidesmas!

I present, for your edification, enlightemnent, enjoyment and possibly also eggplant (as alliteration, assonance and arrogance all annoy, albeit absolutely avoidably), all of the slides that are on my current thinkpad.

If that's why you're primarily here, the index is over there - enjoy. I'll see you back here later. Probably. After you realise the formatting is quite lame, due to me being fairly lazy about the processing process.

On the other hand, said process required nothing but exactly one hundred lines of tcl (so quoth wc, at least), which for the nonce I present here in their delightful entirety:

#!/usr/bin/env tclsh

proc extract {from_file to_file} {

  mtime-guard $from_file $to_file

  file mkdir [ file dirname $to_file ]

  set from [ open $from_file r ]
  set to [ open $to_file w ]

  set emit 0

  while {![eof $from]} {
    set line [gets $from]
    if [regexp {<\/html:textarea>$} $line] {
      set emit 0
    }
    if $emit { puts $to $line }
    if [regexp {^<html:textarea} $line] {
      set emit 1
    }
  }

  close $from
  close $to

  return ok
}

proc target-list {from_dir} {
  return [glob -directory $from_dir -tails */*.xul]
}

proc extract-into {from_dir to_dir file_name} {
  extract $from_dir/$file_name $to_dir/$file_name
}

proc extract-targets {from_dir to_dir} {
  foreach target_file [target-list $from_dir] {
    extract-into $from_dir $to_dir $target_file
  }
}

proc mtime {file_name} {
  if {[file exists $file_name]} {
    file mtime $file_name
  } else {
    expr 0
  }
}

proc mtime-guard {from_file to_file} {
  if {[mtime $to_file] >= [mtime $from_file]} {
    return -level 2 mtime-guard
  }
}

proc markdownify {from_file to_file} {

  file mkdir [ file dirname $to_file ]

  set from [ open $from_file r ]
  set to [ open $to_file w ]

  regexp {([^/]+)/([^/]+).xul$} $from_file - conference title

  puts $to "title: $conference - $title"
  puts $to "description: Slides for the talk $title at $conference"
  puts $to "created: 2012-12-22 00:30:00"
  puts $to ""
  puts $to "## Slides for the talk $title at $conference"
  puts $to ""

  while {![eof $from]} {
    set line [gets $from]
    switch -- $line \
      ---- { puts $to ""; puts $to -; puts $to "" } \
      default { puts $to "    $line" }
  }

  close $from
  close $to
}

proc markdownify-target {from_dir to_dir target_file} {
  if [regexp {([^/]+)-(\d+)[^/]*/([^/]+)\.xul$} $target_file - conf year talk] {
    markdownify $from_dir/$target_file $to_dir/$year/$conf/$talk.md
  } else {
    puts stderr "What the flying fsck was $target_file meant to be?"
  }
}

proc markdownify-targets {from_dir to_dir} {
  foreach target_file [target-list $from_dir] {
    markdownify-target $from_dir $to_dir $target_file
  }
}

puts [eval {*}$argv]

I shall now pause, briefly, while you stop screaming.

...

Calmer now? If not, might I recommend hot ribena - most soothing (other brands may of course be substituted but ribena is a lot easier to ddg or google).

...

Ready? Are we sitting comfortably? Then let us begin.

puts [eval {*}$argv]

(surprise, we're beginning at the ending ... because that makes a better beginning ... no, I'm not drunk, quiet at the back please)

Tcl is a language of strings. In fact, in Tcl everything is a string - I've seen this abbreviated to EIAS, so it's presumably something regularly said. Of course, this is actually a lie told to children in the sense that it's more "everything has a canonical representation as a string and then when you use it as something a C struct with a more useful representation like say an int for an integer or a C array for a list gets created and then it can happily re-use that although it only ever keeps one non string represenation around" but that's both not nearly as pithy and given my limited understanding of the language so far quite possibly not that much more accurate.

So, in Tcl everything is a string. For reasonable values of 'is'.

Each line of a script is a command (modulo \ to spread it over multiple lines and ; to end a command without a \n, but if I keep putting in all the caveats we'll never get anywhere), and that command is a space separated list, the first member of which is the command and the rest of which are the arguments. To execute a command and use the results, ala $() or backticks in shell, one uses []. So

puts [something]

is analogous to perl

print(something());

whreas

puts something

would be analogous to

print('something');

(well, no, actually more like say than print, or perl's -l switch, or ... damnit, I'm caveatting again ... I'll never make evil genius at this rate)

and then {*} basically says "split this and then list flatten", so

puts [eval {*}$argv]

is fairly analagous to

print eval @ARGV;

Clear? No? Bah ... ok, look. Here's why I care, mostly. If I define

proc hello {name} {
  return "Hello, $name"
}

which is roughly

sub hello {
  my ($name) = @_;
  return "Hello, ${name}"
}

then I can do

$ ./myscript hello mst
Hello, mst

wherein the eval calls my hello proc with 'mst' as its only argument. Even better, if I get the arguments wrong:

$ ./myscript hello too many
wrong # args: should be "hello name"
  (and a stack trace, which I shall elide)

so I basically just created a trivial subcommand system with one line of code. Which is what I used to develop the whole thing, up to the point where I ran

$ ./flist extract-targets raw slides
$ ./flist markdownify-targets slides out

and copied the out directory into the shadowcat site content tree. The rest of the code is largely not that interesting once I tell you that the raw files are .xul files that embed a section that looks like

<html:textarea id="builtinCode" style="visibility: collapse"><![CDATA[
Slide 1
Content
----
Slide 2
Content
----
...
]]></html:textarea>

and so the extract code grabs that section, and markdownify converts it into something like -

    Slide 1
    Content

-

    Slide 2
    Content

-

    ...

which renders through to something suitable (the - characters were an easy way to make sure each slide becomes its own pre block rather than them running together; I have vague thoughts I should replace those with some actual notes, or at least some limited form of transcript, but I reserve the right to never get round to doing so).

Probably the only interesting part other than the eval trick is mtime-guard -

proc mtime-guard {from_file to_file} {
  if {[mtime $to_file] >= [mtime $from_file]} {
    return -level 2 mtime-guard
  }
}

wherein the 'return -level 2' part causes the calling procedure to return, so you can prevent re-doing work you've already done with

mtime-guard $if_this_is_older_than $the_target

and your proc will exit right then and there if it is. Of course, then the whole thing ran fast enough that I didn't bother adding it to the markdownify code, but I still find the technique really cute.

Oh, and yes, the original does say 'fsck' too, but only because I already knew I'd be publishing it later.

And as I believe I mentioned earlier, the index is over there - enjoy.

-- mst, out.