自分の.xyzzyに書いているコードや遊びで作ったコードを置いてあります。 再利用していただければ幸いです。 ちなみに、buf2htmlでもう少し見やすくする予定です。
使い方は想像して下さい。
何かを作るときに調べたことを忘れないように覚書としてまとめておきます。 できれば、へなちょこリファレンス向けに書いておきたいです。 適当な単語を用いているので、適宜読み替えて下さい。
基本的にZopeはファイルの編集から全てをブラウザから出来るようになっています。 が、ちゃんとクライアントのエディタで編集するためのプロダクトExternalEditorがあったります。 で、それをxyzzyから直接呼び出すためのものです(もちろんExternalEditorのWindows用helperの導入が必要です)。 C-x C-f で http://host/target で読み込むことができます。
*before-find-file-hook*と*find-file-hook*に引っ掛けています。 重複の読み込みはしないようにチェックしてます。
ブラウザを常時表示しておかないと、毎回パスワードを聞かれるので注意して下さい。 bx:navigateでやるという手もありますが、今回はshell-executeでやってます。 ange-ftpとかWebDAVがサポートできればもう少しスマートに出来ますが、一番作り込みの少ない方向で。
;;; invoke zope object
(defun invoke-zope-object (loc)
(unless (string-matchp "^\\(http://.*\\)/\\([^/]+\\)$" loc)
(return-from invoke-zope-object nil))
(dolist (buf (buffer-list))
(when (string= loc (get-buffer-alternate-file-name buf))
(message "Old Zope Object")
(return-from invoke-zope-object buf)))
(shell-execute
(format nil "~A/externalEdit_/~A"
(match-string 1)
(match-string 2)) t)
(message "invoke ~A" loc)
(throw nil 'silent-quit))
(add-hook '*before-find-file-hook* 'invoke-zope-object)
;;; mark zope object
(defun mark-zope-object ()
(when (string-matchp "^.+-.+-\\(.*\\)\\.\\w+$"
(file-namestring (get-buffer-file-name)))
(set-buffer-alternate-file-name
(concat "http://" (substitute-string (match-string 1) "," "/")))))
(add-hook '*find-file-hooks* 'mark-zope-object)
ソースレビュー時にソースの差分をdiffで採るときに面倒だったので、二画面ファイラから選択できるようにしました。
(defun my-filer-diff-file ()
(let ((file1 (ed::filer-fetch-file nil nil))
(file2 (ed::filer-fetch-file nil t)))
(unless (and file1 file2)
(error "diffするファイルを指定して"))
(diff file1 file2)
(and (find-buffer ed::*diff-new-buffer-name*)
(find-buffer ed::*diff-old-buffer-name*)
(filer-cancel))))
(define-key filer-keymap #\M-f 'my-filer-diff-file)
ミニバッファ内での編集のときに、行数に応じて自動的にenlarge-windowします。 標準であるような気がしないでもないですが、emacsのを見よう見真似で取り合えず。
(defun minibuf-resize ()
(when (minibuffer-window-p (selected-window))
(let ((l (save-excursion
(goto-char (point-max))
(current-line-number)))
(h (window-height)))
(when (< 10 l)
(setq l 10))
(unless (= l h)
(enlarge-window (- l h))
(refresh-screen)))))
(add-hook '*post-command-hook* 'minibuf-resize)
現在は特になし
会社で、sambaは入ってないけどFTPは使える機械で作業させられそうなので なんとかならんかなぁ、と作り始めたもの。結局なんともなりませんでした。 残骸ですが晒しておきます。
ログインして、ファイルを取ってくるだけです。保存はできません。 C-x C-fで、find-file: ftp://www.odn.ne.jp/index.html とかしてください。 他にログインするアカウントに応じてホームディレクトリを変更したりする機能が必要かと思います。 エラー処理も弱いです。*before-find-file-hook*でいろいろやってます。 ちなみに、フック変数は、その過程で調べたものです。
ange-ftpの"ange"は、作者の"Andy"から来ていると最近知ったので、挑戦して見ましたが、 「素直にvi使った方が早かった」ということが分かりました。
なお、実装に当たってはwww/www-ftp.lを参考にさせていただきました。
~/site-lisp/ftpts.lの内容
;;; FTP transparent support
;;; ftpts.l
(require "wip/ftp")
(export '(*ftpts-log-buffer-name*
*ftpts-log-buffer*
ftpts-toggle-log))
(defvar *ftpts-ftp-port* 21
"ftpts: FTPポート番号")
(defvar *ftpts-passive-mode* nil
"ftpts: PASSIVEモードの使用")
(defvar *ftpts-use-log-buffer* nil
"ftpts: ログバッファの使用")
(defvar *ftpts-log-buffer-name* "*ftpts log*"
"ftpts: ログバッファ名")
(defvar *ftpts-log-buffer* nil
"ftpts: ログバッファ")
(defvar *ftpts-user* "anonymous"
"ftpts: ログインユーザ名")
(defvar *ftpts-pass* ""
"ftpts: ログインパスワード")
(defun ftpts-toggle-log ()
(interactive)
(if *ftpts-use-log-buffer*
(delete-buffer *ftpts-log-buffer-name*))
(setq *ftpts-use-log-buffer*
(not *ftpts-use-log-buffer*)))
(defun read-passwd (&optional (prompt "password: ") &key default)
"ftpts: wip/ftp.lのgetpassに初期値を与えられる様に変更"
(let ((pw (make-vector 16
:element-type 'character
:initial-contents default
:fill-pointer (length default)
:adjustable t)))
(loop
(minibuffer-prompt "~A~v@{~a~:*~}" prompt (length pw) #\*)
(let ((c (read-char *keyboard*)))
(case c
(#\RET (return pw))
(#\C-g (quit))
(#\C-q (vector-push-extend (read-char *keyboard*) pw))
(#\C-h (or (zerop (length pw))
(vector-pop pw)))
(t (vector-push-extend c pw)))))))
(defun ftpts-log-buffer ()
"ftpts: ログバッファの取得"
(unless (and *ftpts-log-buffer*
(find-buffer *ftpts-log-buffer-name*))
(when (setq *ftpts-log-buffer*
(switch-to-buffer *ftpts-log-buffer-name*))
(make-local-variable 'kept-undo-information)
(setq kept-undo-information nil)
(make-local-variable 'need-not-save)
(setq need-not-save t)))
*ftpts-log-buffer*)
(defun find-location (loc)
"ftpts: *before-find-file-hook* 用のフック関数"
; マッチするか?
(unless (string-matchp "^ftp://\\([^/:]*\\)\\(:\\([^/]+\\)\\)?/?\\(.*\\)$" loc)
(return-from find-location nil))
; 既に読み込んでいるか?
(dolist (buf (buffer-list))
(if (string= loc (get-buffer-alternate-file-name buf))
(return-from find-location buf)))
; FTP経由で読込み
(let ((host (match-string 1))
(port (match-string 3))
(target (match-string 4))
(*ftp-passive-mode* *ftpts-passive-mode*)
(*standard-output*
(if *ftpts-use-log-buffer*
(make-buffer-stream (ftpts-log-buffer))
*standard-output*)))
(format t "logging to ~A ... now ~A\n" host
(format-date-string "%d %b %Y %H:%M:%S %Z"))
(let* ((user (read-string "name: " :default *ftpts-user*))
(pass (read-passwd "password: " :default *ftpts-pass*))
(ftp (ftp-connect host (or port *ftpts-ftp-port*)))
(name (file-namestring target))
buf)
(unwind-protect
(progn
(ftp-login ftp user pass)
(ftp-ascii ftp)
(switch-to-buffer (setq buf (create-new-buffer name)))
(make-local-variable 'kept-undo-information)
(setq kept-undo-information nil)
(ftp-get ftp target (make-buffer-stream buf))
(cond
((>= (ftp-reply-code ftp) 500)
(delete-buffer buf)
nil)
(t
(setq *ftpts-user* user)
(setq *ftpts-pass* pass)
(set-buffer-modified-p nil buf)
(set-buffer-alternate-file-name loc buf)
(setq kept-undo-information t)
buf)))
(ftp-quit ftp)))))
(add-hook '*before-find-file-hook* 'find-location)
フック変数をまとめておきます。 フック変数しか対応する方法が無かったりするので、あることを知っている方がよいかも。 ~/lispとChangeLog.htmlでgrepに引っ掛ったものだけです。 漏れや誤りがある筈なので、通報をお願いします(MLを見る方が良いことは分かってるんですが、数千件もあるので…)。 下のリストはcsv-modeで編集したものを、html化して貼り付けています。感謝>大久保さん
| 起動時 | ||
|---|---|---|
| *pre-startup-hook* | run-hooks | siteinit.l実行後に実行される。 |
| *init-app-menus-hook* | run-hooks | .xyzzy実行後に実行される。おそらく*app-menu*を直接変更する用途で用いられる。 |
| *load-history-hook* | run-hooks | .xyzzy処理後に実行される。コマンドバー、セッション等のヒストリ変数の読み込みに使用されている。 |
| *post-startup-hook* | run-hooks | .xyzzy実行後に実行される。 |
| *command-line-mailto-hook* | funcall | コマンドラインで指定された -mailto オプションの引数を元に実行される。 |
| *process-command-line-hook* | until-success | xyzzy.exeに渡されたオプション引数を判定する際に呼び出されます。xyzzy 0.2.2.224〜 |
| 終了時 | ||
| *save-history-hook* | run-hooks | *kill-xyzzy-hook*実行時に呼び出される。コマンドバー、セッション等のヒストリ変数の保存に使用されている。 |
| *query-kill-xyzzy-hook* | run-hooks | xyzzy終了時に実行される。このフック変数の実行がnilだと終了しない。[xyzzy:03872]を参照。 |
| *kill-xyzzy-hook* | run-hooks | xyzzy終了時に実行される。 |
| システム | ||
| *pre-command-hook* | run-hooks | コマンドループにおいてコマンドの実行前に実行される。 |
| *post-command-hook* | run-hooks | コマンドループにおいてコマンドの実行後に実行される。 |
| *auto-fill-hook | funcall | auto-fill-modeの時に、self-insert-commandから*last-command-char*を引数として実行される。普通は do-auto-fillが設定されている。多分修正しない方が良い。 |
| auto-fill-hook | run-hooks | 入力の結果、fill-columnを越えたらdo-auto-fillから実行される。 |
| enable-post-buffer-modified-hook | − | post-buffer-modified-hookを有効にする。xyzzy 0.2.1.186〜 [xyzzy:06354]を参照。 |
| post-buffer-modified-hook-enabled-p | − | post-buffer-modified-hookが有効かどうかを判定する。xyzzy 0.2.1.186〜 |
| post-buffer-modified-hook | − | 任意のバッファで任意の操作が行われたときに呼び出される。xyzzy 0.2.1.186〜 |
| バッファ | ||
| *before-find-file-hook* | until-success | find-fileの最初で実行される。 |
| *find-file-file-not-found-hook* | until-success | find-fileで指定されたファイルが存在しないときに実行される。 |
| *find-file-hooks* | run-hooks | find-fileの最後で実行される。 |
| *query-kill-buffer-hook* | while-success | kill-bufferで実行される。バッファの破棄を確認するために使用する。 |
| *before-save-buffer-hook* | until-success | 多分save-bufferの最初に実行される。 |
| *after-save-buffer-hook* | run-hooks | 多分save-bufferの最後に実行される。 |
| *save-buffer-no-filenames-hook* | until-success | 多分get-buffer-file-nameがnilとかだと実行される。 |
| *create-buffer-hook* | run-hooks | 多分create-new-bufferで実行される。 |
| *activate-hook* | run-hooks | バッファがactivateされたときに任意の処理を行うために使用する。デフォルトでは、ファイルの修正日付を確認している。 |
| *deactivate-hook* | run-hooks | バッファがdeactivateされたときに任意の処理を行うために使用する。デフォルトでは、クリップボードの同期を行っている。 |
| *before-delete-buffer-hook* | run-hooks | delete-bufferの直前にで実行される。nilを返すと消されない。 |
| *delete-buffer-hook* | run-hooks | delete-bufferで実行される。 |
| *change-buffer-colors-hook* | funcall | (よく分かりません) |
| *enter-minibuffer-hook* | run-hooks | minibufferに入ったときに実行される。(interactive ...)で指定された引数が渡される。 |
| *exit-minibuffer-hook* | run-hooks | minibufferから出たときに実行される。 |
| *find-file-read-only-hook* | run-hooks | find-file-read-onlyで実行される。 |
| 各種モード | ||
| *calc-mode-hook* | run-hooks | calc-mode起動時に実行される。 |
| *calendar-mode-hook* | run-hooks | calendar起動時に実行される。 |
| *den8-view-mode-hook* | run-hooks | den8viewのden8-summary-mode起動時に実行される。 |
| *den8-draft-mode-hook* | run-hooks | den8viewのden8-draft-mode起動時に実行される。 |
| *log-summary-mode-hook* | run-hooks | 「niftyのログを読む」のlog-summary-mode起動時に実行される。 |
| *log-article-mode-hook* | run-hooks | 「niftyのログを読む」のlog-article-mode起動時に実行される。 |
| *shell-mode-hook* | run-hooks | shell-mode起動時に実行される。 |
| *buffer-menu-mode-hook* | run-hooks | buffer-menu起動時に実行される。 |
| *perl-mode-hook* | run-hooks | perl-mode起動時に実行される。 |
| *pascal-mode-hook* | run-hooks | pascal-mode起動時に実行される。 |
| *fundamental-mode-hook* | run-hooks | fundamental-mode起動時に実行される。 |
| *basic-mode-hook* | run-hooks | basic-mode起動時に実行される。 |
| *csharp-mode-hook* | run-hooks | c#-mode起動時に実行される。 |
| *c-mode-hook* | run-hooks | c-mode起動時に実行される。 |
| *c++-mode-hook* | run-hooks | c++-mode起動時に実行される。 |
| *css-mode-hook* | run-hooks | css-mode起動時に実行される。 |
| *html-mode-hook* | run-hooks | html-mode起動時に実行される。 |
| *idl-mode-hook* | run-hooks | idl-mode起動時に実行される。 |
| *java-mode-hook* | run-hooks | java-mode起動時に実行される。 |
| *latex-mode-hook* | run-hooks | latex-mode起動時に実行される。 |
| *lisp-mode-hook* | run-hooks | lisp-mode起動時に実行される。 |
| *lisp-interaction-mode-hook* | run-hooks | lisp-interaction-mode起動時に実行される。 |
| *sql-mode-hook* | run-hooks | sql-mode起動時に実行される。 |
| *text-mode-hook* | run-hooks | text-mode起動時に実行される。 |
| *view-mode-hook* | run-hooks | view-mode起動時に実行される。 |
| その他 | ||
| *isearch-scanner-hook* | funcall | isearch-scannerで実行される。scan-bufferするパターンを書き換えることが出来そう。 |
| *ime-mode-hook* | run-hooks | imeの切替え時に実行される。 |
| fill-region-hook | run-hooks | fill-region/fill-paragraphの最後で実行される。 |
| *drag-and-drop-hook* | funcall | d&d時に実行される。デフォルトでは、d&d先がミニバッファなら入力として扱い、それ以外ならfind-fileするようになっている。 |
| *grep-hook* | run-hooks | grep起動時に実行される。 |
| *grepd-hook* | run-hooks | grep-dialog起動時に実行される。 |
| *grep-directory-name-hook* | funcall | grep-dialogでgrep対象のディレクトリを個々に設定したい場合に実行される。 |
| *gresreg-directory-name-hook* | funcall | gresreg-dialogでgresreg対象のディレクトリを個々に設定したい場合に実行される。 |
| *diff-mode-hook* | run-hooks | diff起動時に実行される。 |
| *print-completion-list-hook* | funcall | do-completion実行時に、補完リストを表示するために使用されている。ポップアップ表示の前に処理される。 |
| *select-pseudo-frame-hook* | run-hooks | 多分フレームが選択されたときに実行される。 |
| *make-backup-filename-hook* | funcall | (多分触らないほうがいいもの) |
| *command-output-mode-hook* | run-hooks | execute-subprocessの中でcommand-output-mode起動時に実行される。 |
| *show-match-hook* | while-success | 検索で一致したものの表示に使用している。設定されていなければ、show-matchが実行される。 |
| *paste-hook* | run-hooks | クリップボードから貼り付けるときに実行される。 |
| *filer-chdir-hook* | run-hooks | (ファイラでディレクトリが変更されたりすると実行される。用途不明) |
| *pre-abbrev-expand-hook* | run-hooks | expand-abbrevの最初に実行される。 |
| *tail-f-mode-hook* | run-hooks | tail-f起動時に実行される。 |
lispがどのようにシステムに関わっているのかを示します。 誤解を恐れず描いてしまえば、xyzzyは以下の図に構成されています。 基本的にはemacs系と同じだと思います。
┏━━━━━━━━┓┏━━━━━━━━┓┏━━━━━━━┓┏━━━━━┓www-mode ┃ユーザ層 ┃┃初期設定ファイル┃┃ ユーザ関数群 ┃┃ユーザDLL ┃2ch-mode ┃ ┃┃ ┃┃ ┃┃ ┃kamail ... ┗━━━━━━━━┛┗━━━━━━━━┛┗━━━━━━━┛┗━━━━━┛ ┏━━━━┯━━━┓┏━━━━━━━━━━━━━━━━━━━━━━━━┓c-mode ┃アプリ層│lisp層┃┃ 標準関数群 ┃grep ... ┃ │ ┃┃ (ビルトイン関数を含む) ┃ ┃ ┝━━━┫┣━━┯━━━━━━━━━━━━┯━━━━━━━━┫car ┃ │exe層 ┃┃GUI │xyzzy lisp インタプリタ │ビルトイン関数群┃cdr ... ┗━━━━┷━━━┛┗━━┷━━━━━━━━━━━━┷━━━━━━━━┛ ┏━━━━━━━━┓┏━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃OS層 ┃┃ Windows Service ┃ ┃ ┃┠─────┬────────┬─────────┨ ┃ ┃┃ 通信 │ メモリ制御 │ ファイルシステム ┃ ┗━━━━━━━━┛┗━━━━━┷━━━━━━━━┷━━━━━━━━━┛
xyzzy起動時に特定のlispファイルが評価される順番が決まっている以外は、 xyzzy lisp インタプリタから見た場合に、xyzzyの標準関数群/初期設定ファイル/ユーザ関数群の違いは ほとんど無いはずです。 他のマクロ機能を持つエディタとはこの点が根本的に異なり、 xyzzyの拡張性の高さに繋がっていると思います。
起動時のフローです。~/xyzzy.exeを起動すると、 最初はinit.ccから~/lisp/startup.lが呼び出されます。 以降の主なフローは以下のとおりです。
| No. | ファイル | フック変数 |
|---|---|---|
| 1 | siteinit.l | |
| 2 | *pre-startup-hook* | |
| 3 | .xyzzy | |
| 4 | *load-history-hook* | |
| 5 | *init-app-menus-hook* | |
| 6 | *post-startup-hook* |
ディレクトリ構造をそのままポップアップメニューにする例です。 以前xyzzy-MLに投げた物の再録です。アイテムを選択した場合に、任意の関数を実行可能です。 元々電信八号を使ってたときに、MIME部分をもっと簡単に見たいっていう思いから作りました。 でも今は、穂浪さんちのDatula-modeにお世話になってます。
;;; ディレクトリ階層に応じたポップアップメニューの作成
; メニューの作成
(defun create-dir-menu (dlst &optional m)
(long-operation
(let ((mnu (or m (create-popup-menu))) (dn 0) (fn 0))
(dolist (item dlst)
(let ((icar (car item))
(icadr (cadr item)))
(cond ((functionp icadr)
(and (not (zerop dn))
(zerop fn)
(add-menu-separator mnu))
(incf fn)
(add-menu-item mnu 'dir-menu icar icadr))
(t
(incf dn)
(add-popup-menu mnu (create-dir-menu icadr) icar)))))
mnu)))
; ディレクトリリストの作成
(defun create-dir-list (src func)
(let* ((d (directory src :directory-only t :absolute t))
(f (directory src :file-only t))
(dlst (sort d #'string-lessp))
(flst (sort f #'string-lessp)))
(append
(mapcar
#'(lambda (x)
(list (file-namestring (string-right-trim "/\\" x ))
(create-dir-list x func)))
dlst)
(mapcar
#'(lambda (x)
(list x
#'(lambda () (interactive)
(apply func (list (append-trail-slash src) x)))))
flst))))
;;; サンプル
(setq *app-popup-menu*
(create-dir-menu
(create-dir-list "c:/My Documents" 'sample-function)))
(defun sample-function (dir file)
(interactive)
(set-default-directory
(prog1
(default-directory)
(set-default-directory dir)
(file-name-dialog
:save nil
:title "サンプルです"
:default file
:filter '(("いわゆるすべてのファイル(*.*)" . "*.*"))))))
補完を一覧表示出来て便利です。 *Completion*バッファを表示するよりも、Windowsっぽくて好きです。
;;; ポップアップ (require "dabbrev") (global-set-key #\C-\; 'dabbrev-popup) (setq *popup-completion-list-default* :always) (setq *minibuffer-popup-completion-list* :never)
非常に高機能なファイラが搭載されていますが、キーバインドが良く分からないので以下の様に書いておくと便利かも。 ちなみに、ファイラもキーバインドを変更可能です。
;;; ガイドテキストの変更
(setq *filer-guide-text*
'("(A)ttr (B)yteCompile (C)opy (D)elete (E)xtract (F)ind (G)o (J)ump m(K)dir <L>Cd (M)ove re(N)ame"
"c(O)mpress <P>Sync (Q)uit (R)ename (S)ync desk(T)op (U)sage <V>List <W>View <Y>Shortcut <Z>Property"
"<C-g>Quit (C-u)sage (M-g)rep (M-v)iew (M-r)eplace <*>Load <.>Mask <\\>Root <<>Top <>>Bottom <@>Copy <=>Compare <^>Media <]>Send"
"</>Mark_group <Home>Invert <F3>Exec <F6>Sort <C-Home>Invert_all <S-Home>Erase_mark <S-C-Home>Mark_all <Space>Mark"
"<TAB>Switch <End>Update <Enter>Ok <Apps/S-F10>Menu <Backspace>Updir"))
以下はおまけです。
;;; 削除したファイルをゴミ箱へ
(setq *filer-use-recycle-bin* t)
;;; デフォルトのファイルマスクを変更
(setq *filer-primary-file-mask* '("*"))
(setq *filer-secondary-file-mask* '("*"))
;;; My Documentsやデスクトップへ直接ディレクトリ移動できるように追加
(setq *filer-directories*
'((" [スプール]" . "c:/spool")))
(pushnew (cons " [デスクトップ]" (get-special-folder-location :desktop))
*filer-directories*)
(pushnew (cons " [My Documents]" (get-special-folder-location :personal))
*filer-directories*)
WindowsのINIファイルから値を取得するには、Win32APIのGetPrivateProfileString()を使います。 html+-modeで、インターネットショートカットのリンク先を取得するために使用しています。
[InternetShortcut] URL=http://www.microsoft.com/isapi/redir.dll?prd=ie&pver=5.0&ar=IStart Modified=A02F5BEB343DC10148
;;; INIファイルの読み込み
(require "foreign")
(require "wip/winapi")
(c:define-dll-entry
winapi:DWORD GetPrivateProfileString
(winapi:LPCSTR winapi:LPCSTR winapi:LPCSTR
winapi:LPCSTR winapi:DWORD winapi:LPCSTR)
"kernel32" "GetPrivateProfileStringA")
(let* ((sec (si:make-string-chunk "InternetShortcut"))
(key (si:make-string-chunk "URL"))
(non (si:make-string-chunk ""))
(sz 500)
(url (si:make-chunk nil sz))
(file (si:make-string-chunk (map-slash-to-backslash
"c:/WINDOWS/Favorites/MSN.url"))))
(GetPrivateProfileString sec key non url sz file)
(insert (si:unpack-string url 0)))
簡単なWWWサーバです。 eval してから wwwsvr を動かします。 ブラウザから http://localhost/ を参照すると、xyzzyのバッファの一覧を返します。 ファイル名をクリックすると、そのファイルを表示します。
;;; 簡易WWWサーバ
; メイン関数
(defun wwwsvr ()
(interactive)
; バッファ準備
(switch-to-buffer "*wwwsvr log*")
(delete-region (point-min) (point-max))
(make-local-variable 'kept-undo-information)
(setq kept-undo-information nil)
(make-local-variable 'need-not-save)
(setq need-not-save t)
(with-output-to-selected-buffer
(long-operation
(with-open-stream (ls (make-listen-socket nil 80))
(wwwsvr-log "[ server started ]")
(loop
(with-open-stream (sock (accept-connection ls))
(wwwsvr-log "[ connection accepted ]")
(multiple-value-bind (method target)
(wwwsvr-receive sock)
(wwwsvr-send sock method target)))
(wwwsvr-log "[ connection closed ]"))))))
; 受信関数
(defun wwwsvr-receive (sock)
(let (line method target)
(while (and (setq line (read-line sock nil))
(not (string= line "")))
(when (string-match
"^\\(.*\\) /\\([^ ]*\\) HTTP/[0-9]*\.[0-9]*$" line)
(setq method (match-string 1))
(setq target (match-string 2)))
(wwwsvr-log line))
(wwwsvr-log (format nil "request received (~S ~S)" method target))
(values method target)))
; 送信関数
(defun wwwsvr-send (sock method target)
(let (line)
(format sock "HTTP/1.0 200 OK\n\n")
(cond ; 対象が存在する場合
; ※ここで色々するとサーバサイドスクリプトとして
; lispが使えて面白いかも
((find-buffer target)
(with-input-from-buffer ((find-buffer target))
(while (setq line (read-line *standard-input* nil))
(princ line sock)
(princ "\n" sock)))
(wwwsvr-log "[ target sent ]"))
; 無指定の場合
; ※バッファのリストを返す
((string= target "")
(format sock "<html><body><h3>buffers list</h3><hr><ol>\n")
(format sock "~:{<li><a href=\"/~A\">~A</a></li>~}\n"
(mapcar #'(lambda (x) (list x x))
(mapcar 'buffer-name (buffer-list))))
(format sock "</ol><hr></body></html>\n")
(wwwsvr-log "[ buffers list sent ]"))
; 見つからなかった場合
; ※見つからない旨を通知
(t
(format sock "<html><body>missing ~S</body></html>" target)
(wwwsvr-log "[ error message sent ]")))))
; ログ表示用関数
(defun wwwsvr-log (s)
(format t "<~A> ~A\n" (format-date-string "%Y-%m-%d %H:%M:%S") s)
(refresh-screen))