cookie1.el 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. ;;; cookie1.el --- retrieve random phrases from fortune cookie files
  2. ;; Copyright (C) 1993, 2001-2012 Free Software Foundation, Inc.
  3. ;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
  4. ;; Maintainer: FSF
  5. ;; Keywords: games, extensions
  6. ;; Created: Mon Mar 22 17:06:26 1993
  7. ;; This file is part of GNU Emacs.
  8. ;; GNU Emacs is free software: you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; Support for random cookie fetches from phrase files, used for such
  20. ;; critical applications as emulating Zippy the Pinhead and confounding
  21. ;; the NSA Trunk Trawler.
  22. ;;
  23. ;; The two entry points are `cookie' and `cookie-insert'. The helper
  24. ;; function `shuffle-vector' may be of interest to programmers.
  25. ;;
  26. ;; The code expects phrase files to be in one of two formats:
  27. ;;
  28. ;; * ITS-style LINS format (strings terminated by ASCII 0 characters,
  29. ;; leading whitespace ignored).
  30. ;;
  31. ;; * UNIX fortune file format (quotes terminated by %% on a line by itself).
  32. ;;
  33. ;; Everything up to the first delimiter is treated as a comment. Other
  34. ;; formats could be supported by adding alternates to the regexp
  35. ;; `cookie-delimiter'.
  36. ;;
  37. ;; strfile(1) is the program used to compile the files for fortune(6).
  38. ;; In order to achieve total compatibility with strfile(1), cookie files
  39. ;; should start with two consecutive delimiters (and no comment).
  40. ;;
  41. ;; This code derives from Steve Strassmann's 1987 spook.el package, but
  42. ;; has been generalized so that it supports multiple simultaneous
  43. ;; cookie databases and fortune files. It is intended to be called
  44. ;; from other packages such as yow.el and spook.el.
  45. ;;; Code:
  46. ; Randomize the seed in the random number generator.
  47. (random t)
  48. (defconst cookie-delimiter "\n%%\n\\|\n%\n\\|\0"
  49. "Delimiter used to separate cookie file entries.")
  50. (defvar cookie-cache (make-vector 511 0)
  51. "Cache of cookie files that have already been snarfed.")
  52. ;;;###autoload
  53. (defun cookie (phrase-file startmsg endmsg)
  54. "Return a random phrase from PHRASE-FILE.
  55. When the phrase file is read in, display STARTMSG at the beginning
  56. of load, ENDMSG at the end."
  57. (let ((cookie-vector (cookie-snarf phrase-file startmsg endmsg)))
  58. (shuffle-vector cookie-vector)
  59. (aref cookie-vector 0)))
  60. ;;;###autoload
  61. (defun cookie-insert (phrase-file &optional count startmsg endmsg)
  62. "Insert random phrases from PHRASE-FILE; COUNT of them.
  63. When the phrase file is read in, display STARTMSG at the beginning
  64. of load, ENDMSG at the end."
  65. (let ((cookie-vector (cookie-snarf phrase-file startmsg endmsg)))
  66. (shuffle-vector cookie-vector)
  67. (let ((start (point)))
  68. (insert ?\n)
  69. (cookie1 (min (- (length cookie-vector) 1) (or count 1)) cookie-vector)
  70. (insert ?\n)
  71. (fill-region-as-paragraph start (point) nil))))
  72. (defun cookie1 (arg cookie-vec)
  73. "Inserts a cookie phrase ARG times."
  74. (cond ((zerop arg) t)
  75. (t (insert (aref cookie-vec arg))
  76. (insert " ")
  77. (cookie1 (1- arg) cookie-vec))))
  78. ;;;###autoload
  79. (defun cookie-snarf (phrase-file startmsg endmsg)
  80. "Reads in the PHRASE-FILE, returns it as a vector of strings.
  81. Emit STARTMSG and ENDMSG before and after. Caches the result; second
  82. and subsequent calls on the same file won't go to disk."
  83. (let ((sym (intern-soft phrase-file cookie-cache)))
  84. (and sym (not (equal (symbol-function sym)
  85. (nth 5 (file-attributes phrase-file))))
  86. (yes-or-no-p (concat phrase-file
  87. " has changed. Read new contents? "))
  88. (setq sym nil))
  89. (if sym
  90. (symbol-value sym)
  91. (setq sym (intern phrase-file cookie-cache))
  92. (message "%s" startmsg)
  93. (save-excursion
  94. (let ((buf (generate-new-buffer "*cookie*"))
  95. (result nil))
  96. (set-buffer buf)
  97. (fset sym (nth 5 (file-attributes phrase-file)))
  98. (insert-file-contents (expand-file-name phrase-file))
  99. (re-search-forward cookie-delimiter)
  100. (while (progn (skip-chars-forward " \t\n\r\f") (not (eobp)))
  101. (let ((beg (point)))
  102. (re-search-forward cookie-delimiter)
  103. (setq result (cons (buffer-substring beg (match-beginning 0))
  104. result))))
  105. (kill-buffer buf)
  106. (message "%s" endmsg)
  107. (set sym (apply 'vector result)))))))
  108. (defun read-cookie (prompt phrase-file startmsg endmsg &optional require-match)
  109. "Prompt with PROMPT and read with completion among cookies in PHRASE-FILE.
  110. STARTMSG and ENDMSG are passed along to `cookie-snarf'.
  111. Optional fifth arg REQUIRE-MATCH non-nil forces a matching cookie."
  112. ;; Make sure the cookies are in the cache.
  113. (or (intern-soft phrase-file cookie-cache)
  114. (cookie-snarf phrase-file startmsg endmsg))
  115. (completing-read prompt
  116. (let ((sym (intern phrase-file cookie-cache)))
  117. ;; We cache the alist form of the cookie in a property.
  118. (or (get sym 'completion-alist)
  119. (let* ((alist nil)
  120. (vec (cookie-snarf phrase-file
  121. startmsg endmsg))
  122. (i (length vec)))
  123. (while (>= (setq i (1- i)) 0)
  124. (setq alist (cons (list (aref vec i)) alist)))
  125. (put sym 'completion-alist alist))))
  126. nil require-match nil nil))
  127. ; Thanks to Ian G Batten <BattenIG@CS.BHAM.AC.UK>
  128. ; [of the University of Birmingham Computer Science Department]
  129. ; for the iterative version of this shuffle.
  130. ;
  131. ;;;###autoload
  132. (defun shuffle-vector (vector)
  133. "Randomly permute the elements of VECTOR (all permutations equally likely)."
  134. (let ((i 0)
  135. j
  136. temp
  137. (len (length vector)))
  138. (while (< i len)
  139. (setq j (+ i (random (- len i))))
  140. (setq temp (aref vector i))
  141. (aset vector i (aref vector j))
  142. (aset vector j temp)
  143. (setq i (1+ i))))
  144. vector)
  145. (provide 'cookie1)
  146. ;;; cookie1.el ends here