Saturday, February 7, 2009

Fun with RSS

Today I've been playing around with emacs, and one of the things I've wanted to write is something to grab a web page, do some manipluations to it and dump that to a buffer.

So what I've got going is two things that are not quite joined up yet. Firstly grabbing some URL and sticking it in a buffer...

(defun url-to-buffer(url)
(interactive "sEnter site url : ")
(let ((buffer (get-buffer-create "*url-to-buffer*")))
(shell-command (format "c:/coreutils/bin/wget.exe -q -O - %s" url) buffer)))

As you can see I'm using windows here, but as long as you have a path to wget this should work. I'm simply running wget and capturing the output to a buffer.

So running that on BBC's news RSS feed, I then saved it to a file called bbc.xml.

Since this is an XML file I can parse it without any effort using xml.el. Here is some code that walks through the items in the RSS file and prints them out in human readable format (ie: not XML) into a buffer.

There is some helpful info about xml.el here

which was neccesary to figure to figure out the syntax to grab the text from a tag. I wrote helper function to go from a node to the text for that node, since the syntax is quite verbose:

(defun get-item(tag node)
(let ((child-node (car (xml-get-children node tag))))
(car (xml-node-children child-node))))

This lets you go from a tag name (eg title) and a node that you parsed from the xml file, to the item.

(defun parse-rss(filename)
(interactive "fRss file :")
(let ((parsed-xml
(xml-parse-file filename))
(get-buffer-create (format "%s-parsed.txt" filename))))
(goto-line 1 buffer)
(let* ((rss (car parsed-xml))
(channel (xml-get-children rss 'channel))
(items (xml-get-children (car channel) 'item)))
(dolist (item items)
(let ((title (get-item 'title item))
(description (get-item 'description item))
(date (get-item 'pubDate item)))
(insert (format "*%s*\n%s\n%s\n\n" title description date)))))))

xml-parse-file is our one shot call to parse the xml file, which returns a nested lisp structure representing the xml document. I grab the channel, then grab any items within the channel and print out three items that I've grabbed from them, the title, description and date.

So each item output looks like this:

*Obama defends economic stimulus*
The US president defends his economic stimulus plan as "absolutely necessary", and urges Congress to approve it quickly.
Sat, 07 Feb 2009 22:23:20 GMT

Note that if you want to read RSS feeds in emacs, look up the builtin function newsticker. All this information I'm writing about is really useful if you want to code up something custom though.

Sending email via gmail in emacs

I got this working in linux, and it's fairly easy. I'm still trying to get it working on my win32 machines, and I'll post about that later if I get it working.

This post has most of the details I needed:

So I put the following in my .emacs

; install starttls from here (no need for patch)

(setq send-mail-function 'smtpmail-send-it
message-send-mail-function 'smtpmail-send-it
'(("" 587 nil nil))
(expand-file-name "~/.authinfo")
smtpmail-default-smtp-server ""
smtpmail-smtp-server ""
smtpmail-smtp-service 587
smtpmail-debug-info t
starttls-extra-arguments nil
smtpmail-warn-about-unknown-extensions t
starttls-use-gnutls nil)

And followed the instructions to make the .authinfo file containing:

machine login [your name] password [your password]

And finally download, unzip, make and install startttls:

You don't need to apply the patch there, it works without as far as I can tell.

Finally you can create and send mail.

Friday, January 23, 2009

Simple perforce checkout in emacs using elisp

I know there are all kinds of clever perforce modes, but I prefer to have a few common perforce actions that I write myself and can easily extend and customise as needed.

For example this is all you need to checkout a file you are visiting.

(defun p4-checkout-buffer()
"Check out the current buffer from perforce using a shell command line"
(shell-command (format "p4.exe edit %s" (buffer-file-name)))
(toggle-read-only -1))

Monday, January 12, 2009

Finding writable files in a directory in a windows command line

Simple thing, but if you want to find which files in a directory are writable, use this dos command ...

dir /a-r-d /s /b

/a is to search for attributes. In this case r is read only and d is directory. The minus signs negate those attributes. So we're looking for writable files only.

/s means recurse subdirectories

/b means bare format. Path and filename only.

Thursday, January 8, 2009

Who changed the line your working on last?

My team use Perforce for version control, and I recently wrote an implementation of 'blame', also known as 'praise'. When editing a file that is in Perforce, I can run p4-blame, and it will figure out the last change list that this file, and the line that point is on, was changed. It then opens a buffer containing that change list. This is often useful while working, and is so fast when accessed from the editor, it makes it trivial to do.

First, I wanted it to open two buffers, one with the annotated file (the file you're working on with the change list number at the start of each line), and another with the changelist you're interested in. When running the command multiple times I want these buffers to be deleted. So I added a helper function that creates a named buffer, and if it exists already, makes sure it is empty.

(defun get-buffer-create-and-clear(name)
(interactive "sName: ")
(let ((buffer (get-buffer-create name)))
(goto-line 1 buffer)

Ok now for the blame code. As you can see, mostly what it's doing is called the P4 command line and passing the output to buffers. Firstly I call annotate to get the change list numbered lines. Then I go to that file and go to the same line that point was at. (number-at-point) returns that number. Finally I call p4 describe to get the changelist information.

(defun p4-blame()
"blame the current line of code... it seeks out the change list of the last person
to change this line. The arguments to annotate, which dumps the source file with the
change list number it was last altered in, are q (no header) i (follow branches) and
c for change numbers rather than revision numbers."
(let* ((line (line-number-at-pos))
(source-file (buffer-file-name))
(annotate-buffer (get-buffer-create-and-clear "*p4-annotate*")))
(shell-command (format "p4.exe annotate -q -i -c %s" source-file) annotate-buffer)
(goto-line line annotate-buffer)
(let ((change-number (number-at-point)))
(if change-number
(let ((blame-buffer (get-buffer-create-and-clear "*p4-blame*")))
(shell-command (format "p4.exe describe %d" change-number) blame-buffer))
(message "error: could not get change list number for line %d" line)))))

The error detection is a bit sloppy here. I don't know how to figure out if the shell-command failed. But it's not a big deal because if you do this in a file that is not in P4 then it will simply print a message saying could not get change list.

Ideally I could send the error output to a buffer, and then parse it to see if it's empty.