Miscellaneous code

Sorting FireFox bookmarks in less than 100 lines of Python

I wanted my bookmarks in sorted order. This appears to be a very difficult task, as even Firefox itself can't do it properly. It sorts the folders indiscriminately with the items, which is annoying. It sorts without ignoring case, so it sorts ABCabc, which is also annoying. There's "bookmark managers" that also happen to be able to sort bookmarks, but I don't want that. There's very little out there on the net. There's some Java and PERL scriptlets, but they don't work. There used to be a Firefox plugin but it's gone.

So I wrote this filter which does it in less than 100 lines of Python FULLY COMMENTED. Or about 70 lines sans comments. It uses HTMLParser and a nifty "folder stack" algorithm.

USB add/remove sounds and automounting script

Here's scripts that make noises when you add or remove a USB device. The usb_automount script can automount various USB storage devices in various specific places. I have it mount a particular USB key in one place, each slot of my 4-port media card reader in its own place, and any other device somewhere else. This script, usb_add, and usb_remove are all called by /etc/udev/rules.d/90-local-late.rules when udev detects a new device.

Envelope Address Printer

This is a Python filter to emit the Postscript necessary to print an addressed letter envelope, complete with POSTNET barcode and return address.

Make up a text file with the 3 or 4 line address, or you can type it into stdin yourself, and pipe the output to lpr. You can easily change the return address, margins, font, paper size and other customizations in the beginning of the script.

The address comes out looking like:

address with POSTNET barcode

That series of bars below the last line of the address is the USPS Standard C840 "Barcoding Standards for Letters and Flats" AKA POSTNET (POSTal Numeric Encoding Technique) barcode used by high speed automatic sorting machines to send your letter to the correct post office. You can print it directly under the address or along the lower edge of the envelope.

Here's the scripts I use to maintain these pages:

siteindex Create the "updates" page by listing each file's last updated date. Notice that it extracts the timestamp from within HTML files since those get modified by things other than editing the content.
sitenavMaintain the navigation code. You'll notice it has a version number embedded in an HTML comment. The page is only updated if this is missing or doesn't match. This also means that if you update the version in the script, then all the pages get updated with the new code. There are default positions, so you can write a new page from scratch without worrying about having to conform to any formatting rules.
Note that the "siteindex" script does not emit any navigation HTML, and this script inserts that.
This now uses dynamic HTML to create the pop-up table. If the table is too long to fit in the window, it creates a new double-wide version that's much shorter. Now people with lower resolution monitors can navigate the site.
A nice side-effect is that the navigation javascript is in its own file, and if you add/delete a page, you just update its list of the page links without having to update every single HTML page.
sitecopyKeep the server copy synchronized with the local copy.
publishCall the other four, and in that order too!

And here's the snippet from my .emacs:

;; HTML mode
(defun html-fill ()
  (interactive)
  (save-excursion
    ;; search for previous tag
    (let ((a (search-backward ">"))
          (b nil))
      ;; is it a break tag?
      (if (equal (buffer-substring-no-properties (- a 3) (1+ a)) "<br>")
          (progn
            (setq a (1+ a))
            ;; is there not a line break after it?
            (if (not (equal (buffer-substring-no-properties a (1+ a)) "\n"))
                (progn
                  ;; make it so!
                  (goto-char a)
                  (insert "\n")))))
      ;; find next tag
      (setq b (1- (search-forward "<")))
      ;; back off any line break (very sad code)
      (if (equal (buffer-substring-no-properties b (1+ b)) "\n")
          (setq b (1- b)))
      ;; fill it
      (fill-region a (1- b)))))

(add-hook 'html-mode-hook
          '(lambda nil
             ;; update timestamp when saved
             (autoload 'time-stamp "time-stamp")
             (add-hook 'write-file-hooks 'time-stamp nil t)
             (set (make-local-variable 'time-stamp-active) t)
             (set (make-local-variable 'time-stamp-format) "%02d-%03B-%:y %02H:%02M:%02S")
             (set (make-local-variable 'time-stamp-start) "<!-- Last Update:[ 	]+\\\\?[\"<]+")
             (set (make-local-variable 'time-stamp-end) "\\\\?[\">] -->")
             ;; our special paragraph fill
             (auto-fill-mode 1)
             (local-set-key "\M-q" 'html-fill)
             ;; yes we like small fonts
             (setq fill-column 195)))

Other stuff

ctalk is a script to announce the time. It makes a useful cron job, and I originally wrote it as an alarm clock.

You'll also need the wavplay binary (or another WAV player) and the number WAVs in wavs.tar.gz placed in /usr/share/sounds.

Valid HTML 4.01 Transitional