| (defvar fcookie-index-spec
'((:version u32)
(:count u32)
(:longest u32)
(:shortest u32)
(:flags u32)
(:delim u8)
(:ignored fill 3)
(:offset repeat (:count)
(:foo u32)))
"Description of a fortune cookie index file's contents.")
(defun fcookie (cookies &optional index)
"Display a random fortune cookie from file COOKIES.
Optional second arg INDEX specifies the associated index
filename, which is by default constructed by appending
\".dat\" to COOKIES. Display cookie text in possibly
new buffer \"*Fortune Cookie: BASENAME*\" where BASENAME
is COOKIES without the directory part."
(interactive "fCookies file: ")
(let* ((info (with-temp-buffer
(insert-file-contents-literally
(or index (concat cookies ".dat")))
(bindat-unpack fcookie-index-spec
(buffer-string))))
(sel (random (bindat-get-field info :count)))
(beg (cdar (bindat-get-field info :offset sel)))
(end (or (cdar (bindat-get-field info
:offset (1+ sel)))
(nth 7 (file-attributes cookies)))))
(switch-to-buffer
(get-buffer-create
(format "*Fortune Cookie: %s*"
(file-name-nondirectory cookies))))
(erase-buffer)
(insert-file-contents-literally
cookies nil beg (- end 3))))
(defun fcookie-create-index (cookies &optional index delim)
"Scan file COOKIES, and write out its index file.
Optional second arg INDEX specifies the index filename,
which is by default constructed by appending \".dat\" to
COOKIES. Optional third arg DELIM specifies the unibyte
character which, when found on a line of its own in
COOKIES, indicates the border between entries."
(interactive "fCookies file: ")
(setq delim (or delim ?%))
(let ((delim-line (format "\n%c\n" delim))
(count 0)
(max 0)
min p q len offsets)
(unless (= 3 (string-bytes delim-line))
(error "Delimiter cannot be represented in one byte"))
(with-temp-buffer
(insert-file-contents-literally cookies)
(while (and (setq p (point))
(search-forward delim-line (point-max) t)
(setq len (- (point) 3 p)))
(setq count (1+ count)
max (max max len)
min (min (or min max) len)
offsets (cons (1- p) offsets))))
(with-temp-buffer
(set-buffer-multibyte nil)
(insert
(bindat-pack
fcookie-index-spec
`((:version . 2)
(:count . ,count)
(:longest . ,max)
(:shortest . ,min)
(:flags . 0)
(:delim . ,delim)
(:offset . ,(mapcar (lambda (o)
(list (cons :foo o)))
(nreverse offsets))))))
(let ((coding-system-for-write 'raw-text-unix))
(write-file (or index (concat cookies ".dat")))))))
|