コンテンツへスキップ
2011/05/28 / highmt

clojureで名前空間つきリードマクロをつくる

環境: Clojure 1.2.0

お約束ですが、やっちゃいけません。

実際、Paul Graham がリードマクロの利点のひとつとしてあげている
read が再帰的に処理される、という例
(http://www.komaba.utmc.or.jp/~flatline/onlispjhtml/readMacros.html)

user=> ''a
(quote a)

は、自分でマクロ展開すれば実現できますし、

user=> (defmacro q [form] (list 'quote (macroexpand form)))
#'user/q
user=> (q (q a))
(quote a)

ほんとうにリード時の処理が必要!ってなる場面は少ないような気がします。

ユーザー定義リードマクロが好ましくないとする理由はいろいろあるようですが、
ひとつに名前空間のサポートがない、ということがあります。
(http://clojure-log.n01se.net/date/2008-11-06.html)

ということで名前空間つきリードマクロ(namespaced reader macro)を
試みてみました。

これって cl-annot な方向なのかも…

;;;
;;; (ns rdm
;;;   (:require clojure.contrib.def))
;;;

(defn dispatch-reader-macro
  "see:
  [http://briancarper.net/blog/449/clojure-reader-macros
   http://rd.clojure-users.org/entry/view/56001]"
  [ch fun]
  (let [dm (.get (doto (.getDeclaredField clojure.lang.LispReader "dispatchMacros")
                   (.setAccessible true))
                 nil)]
    (aset dm (int ch) fun)))

(defn true-sym
  "a helper function to treat namespace aliases."
  [ns sym]
  (symbol (str (if-let [t-ns ((ns-aliases *ns*) ns)] t-ns ns))
          (str sym)))

(defn find-true-var
  "a helper function to treat var renamings."
  [sym]
  (if-let [ns (.getNamespace sym)]
    (find-var (true-sym (symbol ns)
                        (symbol (.getName sym))))
    ((ns-map *ns*) sym)))

(defn get-ns-reader-macro-fun
  "a helper function to get a var of the readermacro."
  [form]
  (cond
   (symbol? form) (find-true-var form)
   (list? form) (recur (first form))
   :else (throw (Exception. (str "read error: " form)))))

(defn get-ns-reader-macro-args
  "a helper function to get args of the readermacro."
  [form]
  (cond
   (symbol? form) nil
   (list? form) (rest form)
   :else (throw (Exception. (str "read error: " form)))))

(defn read-form
  "a helper function to read a form following the readermacro."
  [rdr]
  (clojure.lang.LispReader/read rdr true nil true))

(defn dispatch-ns-reader-macro
  "a helper function to apply a var of the readermacro to args of it."
  [rdr letter-sharp]
  (let [rmsym (read-form rdr)
        rmfun (get-ns-reader-macro-fun rmsym)
        rmargs (get-ns-reader-macro-args rmsym)]
    (if rmfun
      (apply rmfun rdr rmsym rmargs)
      (throw (Exception. (str "reader macro not defined: " rmsym))))))

(dispatch-reader-macro \# dispatch-ns-reader-macro)

(defmacro defreadermacro
  "defines the readermacro.
usage:
  ##name form
  ##(name & args) form
name can be ns/name."
  {:arglists '([name args & body])}
  [name & macro-args]
  (let [[mname# margs#] (clojure.contrib.def/name-with-attributes name macro-args)]
  `(defn ~mname# ~margs#)))


;;;
;;; client
;;;

(defreadermacro uppercase-string
  "makes string uppercase."
  [rdr sym & args]
  (let [c (.read rdr)]
    (if (= c (int \"))
      (.toUpperCase (.invoke
                     (clojure.lang.LispReader$StringReader.)
                     rdr
                     c))
      (throw (Exception. (str "read error: " (char c) args))))))
;;user> ##uppercase-string"a"
;;"A"

(defreadermacro my-quote
  "simulates quote."
  [rdr sym & args]
  (let [form (read-form rdr)]
    (list 'quote form)))
;;user> ##my-quote ##my-quote a
;;(quote a)

(defreadermacro my-wrap
  "something a little more complicated."
  [rdr sym & args]
  (let [form (read-form rdr)
        wrapper (first args)]
    (if (list? wrapper)
      `(~@wrapper ~form)
      `(~wrapper ~form))))

;;user> ##(my-wrap (let [x 1])) (+ x 1)
;;2
;;user=> (in-ns 'user2)
;;#<Namespace user2>
;;user2=> (clojure.core/refer-clojure)
;;nil
;;user2=> ##(user/my-wrap (let [x 1])) (+ x 1)
;;2
;;user2=> (alias 'user1 'user)
;;nil
;;user2=> ##(user1/my-wrap (let [x 1])) (+ x 1)
;;2
;;user2=> (refer 'user :rename {'my-wrap 'wrap})
;;nil
;;user2=> ##(wrap (let [x 1])) (+ x 1)
;;2

read-form 以外にもいろんな read-* を提供するようにすれば
(で binding で rdr 隠すとかして呼びやすくすれば)
そんなにわかりにくくない形でユーザー定義リードマクロを導入できるのかも。

ちなみに最初はリードマクロの実体にマルチメソッドを使って
varを作らないようにしていたのですが
varをつくらないと refer, rename の解決ができない
(というかそもそも refer の対象にすらならない)
ということが判明しあえなく却下。
refer ってシンボル間の関係じゃなくて var へのマッピングを追加するんだ
ということをあらためて思い知らされました。

2011/05/11 / highmt

続 clojureで継続モナドを使う

前回の続きです。
stateモナド変換子を使ってもうちょっとすっきりさせてみました。

(use '[clojure.contrib.monads])

(defn pass
  ([]
     (pass nil))
  ([v]
     (fn [s]
       (with-monad cont-m
         (m-result [v s]))))) 

(defn fail []
  (fn [s]
    (if s (s) ((pass) s))))

(defn choose-next [cc s]
  (if s
    (s)
    (cc [nil nil])))

(defn choose
  ([choices]
     (fn [s]
       (with-monad cont-m
         (call-cc
          (fn [cc] (choose cc choices s))))))
  ([cc choices s]
     (if (empty? choices)
       (choose-next cc s)
       (cc [(first choices) #(choose cc (next choices) s)]))))

(defn run-choose [m]
  (run-cont
   (m (fn []
        (with-monad cont-m
          (m-result [nil nil]))))))

(defn test-state-t []
  (run-choose
   (domonad (state-t cont-m)
     [v1 (choose [1 2 3])
      v2 (choose [7 8 9])
      v  (if (or (some nil? [v1 v2]) (odd? (* v1 v2)))
           (fail)
           (pass [v1 v2]))]
     v)))

;; example:
;; user=> (test-state-t)
;; [[1 8] #<user$choose$fn__1554 user$choose$fn__1554@55104da7>]
;; user=> (run-cont ((fnext *1)))
;; [[2 7] #<user$choose$fn__1554 user$choose$fn__1554@1c6745b9>]
;; user=> (run-cont ((fnext *1)))
;; [[2 8] #<user$choose$fn__1554 user$choose$fn__1554@430a14ad>]
;; user=> (run-cont ((fnext *1)))
;; [[2 9] #<user$choose$fn__1554 user$choose$fn__1554@67afe460>]
;; user=> (run-cont ((fnext *1)))
;; [[3 8] #<user$choose$fn__1554 user$choose$fn__1554@28fe53cf>]
;; user=> (run-cont ((fnext *1)))
;; [nil nil]

このへんになると型チェック機構がないとかなり厳しい感じがします…。
なんとかならないかなー。

ただ、いざ問題が起きたときにコンパイル時エラーと実行時エラーとどっちが問題を調べやすいか、というと、
場合によっては実行時エラーのほうが気が楽なこともあるかもしれないです。
コンパイル時の動作にトレース入れたりとかは実行時に比べると敷居が高い感じがするので…。

2011/05/08 / highmt

clojureで継続モナドを使う

環境:
Clojure 1.2.0

非決定性を扱うにはいろいろな選択肢がありますが、そのひとつとして継続での実装があると思います。
clojureで継続を使うには On Lisp 風の継続渡しマクロを使った実装などが考えられますが、
今回は継続モナドの使い方のサンプルもかねて継続モナドで choose / fail を書いてみました。
連続して失敗する選択肢が多いとスタックが溢れるので実用にはなりません。
工夫したらなんとかなるんだろうか…。
陽に restart を返してたりとかあんまり継続を使うメリットいかせてないし…。
新しくモナド書いてうまく隠せばいけそうな気もするけど今の力ではとても大掛かりな感じがするし…。
そもそもモナドを使うのであれば素直にシーケンスとかを使ったほうが
m-zeroとか使ってもっとシンプルに書けるし対角化シーケンスとかの応用もできるしよさげです多分。

(use '[clojure.contrib.monads])

(defn choose
  "Chooses one from specified choices.
  Returns a function to try the next choice for each choices."
  ([choices]
     (with-monad cont-m
       (call-cc
        (fn [cc]
          (m-result (choose cc choices))))))
  ([cc choices]
     (if (empty? choices)
       [nil nil]
       [(first choices) #(cc (choose cc (next choices)))])))

(defn pass
  "Falls through to pass the choice."
  ([] (pass nil))
  ([v] (with-monad cont-m (m-result v))))

(defn merge-restarts
  "Calls restart to go back to the next choice."
  [restarts]
  #(if-let [restart (some identity (reverse restarts))]
     (restart)
     (pass true)))

(defn test-choose
  "Gets choices from the function 'choose' and returns a passed result.
  The returned result contains a function to try the next choice."
  []
  (run-cont (domonad cont-m
              [[v1 restart1 :as vr1] (choose [1 2 3])
               [v2 restart2 :as vr2] (choose [5 6 7])
               :let [v (if (not-any? nil? [v1 v2]) (* v1 v2))
                     fail (merge-restarts [restart1 restart2])]
               ;; sample test
               eos (if (or (nil? v) (odd? v))
                     (fail)
                     (pass))]
              ;; passed result
              [[v1 v2 v] (if-not eos fail)])))

(defn do-test-choose []
  "Collects results from the function 'test-choose' and returns lazy-seq of them."
  (letfn [(step [[v restart]]
            (lazy-seq
             (if restart (cons v (step (run-cont (restart)))) nil)))]
    (step (test-choose))))

;;; get lazy-seq
(do-test-choose)
;; user=> (do-test-choose)
;; ([1 6 6] [2 5 10] [2 6 12] [2 7 14] [3 6 18])

;;; or step manually in repl
(test-choose)
;; user=> (test-choose)
;; [[1 6 6] #<user$choose$fn__917 user$choose$fn__917@6155035a>]
(run-cont ((fnext *1)))
;; user=> (run-cont ((fnext *1)))
;; [[2 5 10] #<user$choose$fn__917 user$choose$fn__917@72a60191>]
;; user=> (run-cont ((fnext *1)))
;; [[2 6 12] #<user$choose$fn__917 user$choose$fn__917@41697023>]
;; ...

2011/04/16 / highmt

emacs で javascript を整形する

環境:
GNU Emacs 23.2.1 (i386-mingw-nt6.1.7601)

emacs上での javascript 編集環境といえば js2-mode なわけですが、
整形コマンドが見当たりません。

というわけで以下で。
といっても
javaプロパティファイルを変換するconf-javaprop-mode拡張
とおなじことをやるだけです。
なので、タイトルに偽りがあって emacs から 単に整形コマンドを呼ぶだけです。
おなじことやるんならマクロかけという話もあるんですが手抜きです。情けない。

  1. jsbeautifierを入手。
    https://github.com/einars/js-beautify
  2. jsbeautifier.pyを呼ぶスクリプトをつくる。
    自分のemacsは、
    (setq shell-file-name “sh”) な環境なので、
    な環境なので、

    #!/bin/sh
    python c:/js-beautify/jsbeautifier.py "$@"
    

    とか。

  3. js-beautify を emacs から呼び出す本体をつくる。
    ;;; js-beautify.el
    
    (defcustom js-beautify-jsbeautifier-path "jsbeautifier"
      "js-beautify jsbeautifier command path."
      :type 'string
      :group 'js-beautify)
    
    (defcustom js-beautify-jsbeautifier-option "-i"
      "js-beautify jsbeautifier command option."
      :type 'string
      :group 'js-beautify)
    
    (defvar js-beautify-mode-map
      (let ((m (make-sparse-keymap)))
        (define-key m "\C-cc" 'js-beautify-convert-buffer)
        (define-key m "\C-cr" 'js-beautify-convert-region)
        m))
    
    
    (defun js-beautify-convert-region-to-1 (rev start end buff)
      (let ((command (concat js-beautify-jsbeautifier-path " " js-beautify-jsbeautifier-option)))
        (save-excursion
          (shell-command-on-region start end command buff nil "*js-beautify-error*" t))))
    
    (defun js-beautify-convert-region-to (rev start end buff)
      (let ((coding-system-for-read 'japanese-shift-jis-dos)
            (coding-system-for-write 'japanese-shift-jis-unix))
        (js-beautify-convert-region-to-1 rev start end buff)))
    
    (defun js-beautify-convert-region-to-buffer (rev start end)
      (interactive "P\nr")
      (js-beautify-convert-region-to rev start end (get-buffer-create "*js-beautify*")))
    (defun js-beautify-convert-buffer-to-buffer (rev)
      (interactive "P")
      (js-beautify-convert-region-to-buffer rev (point-min) (point-max)))
    
    (defun js-beautify-convert-region (rev start end)
      (interactive "P\nr")
      (js-beautify-convert-region-to rev start end t))
    (defun js-beautify-convert-buffer (rev)
      (interactive "P")
      (js-beautify-convert-region rev (point-min) (point-max)))
    
    (add-hook 'js2-mode-hook
              (lambda ()
                (let ((m (copy-keymap js-beautify-mode-map)))
                  (set-keymap-parent m js2-mode-map)
                  (use-local-map m))))
    
    (provide 'js-beautify)
    

    一応revが指定できるみたいに書いてありますが rev はなんの役割も果たしてません。
    前回のjprop.elをそのまま使って手抜きしてるだけです。
    自分の環境は (setq default-process-coding-system ‘(utf-8-dos . japanese-shift-jis-unix ))
    というちょっと変則的な環境なのでエンコーディングを明示してたりします。
    あと、js2-modeを前提としてたりします。

とここまで書いて、こんなおおげさなことをしなくても
M-| で ふつうに shell-command-on-region で jsbeautifier を呼べばいいんだよなー
と思ったのは内緒です。windows はパスとかいろいろ面倒なんです。

2011/04/08 / highmt

javaプロパティファイルを変換するconf-javaprop-mode拡張

環境:GNU Emacs 23.2.1 (i386-mingw-nt6.1.7601)

たいしたもんじゃありませんが
eclipseを立ち上げるまでもないちょっとした確認をしたいときに…

(require ‘jprop) すれば、 conf-javaprop-mode に
\C-cc:バッファ全体を日本語へ変換した結果を*jprop*バッファに出力する
\C-cr:選択範囲を日本語への変換結果を*jprop*バッファに出力する
などが追加されます。
プレフィクスつければ逆になります。

あとは jprop-native2ascii-path を変更するなり
jprop-mode-map を変更するなり
適当にアドバイズするなり
全面的に書き換えるなり。

;;; jprop.el

(defcustom jprop-native2ascii-path
  (let ((command "native2ascii")
        (jdk-path (getenv "JAVA_HOME")))
    (if jdk-path
        (expand-file-name command (expand-file-name "bin" jdk-path))
      command))
  "jdk native2ascii command path."
  :type 'string
  :group 'jprop)

(defvar jprop-mode-map
  (let ((m (make-sparse-keymap)))
    (define-key m "\C-cc" 'jprop-convert-buffer-to-buffer)
    (define-key m "\C-cr" 'jprop-convert-region-to-buffer)
    m))


(defun jprop-convert-region-to-1 (rev start end buff)
  (let ((command (concat jprop-native2ascii-path (if rev "" " -reverse"))))
    (save-excursion
      (shell-command-on-region start end command buff nil "*jprop-error*" t))))

(defun jprop-convert-region-to (rev start end buff)
  (let ((coding-system-for-read 'japanese-shift-jis-dos)
        (coding-system-for-write 'japanese-shift-jis-unix))
    (jprop-convert-region-to-1 rev start end buff)))

(defun jprop-convert-region-to-buffer (rev start end)
  (interactive "P\nr")
  (jprop-convert-region-to rev start end (get-buffer-create "*jprop*")))
(defun jprop-convert-buffer-to-buffer (rev)
  (interactive "P")
  (jprop-convert-region-to-buffer rev (point-min) (point-max)))

(defun jprop-convert-region (rev start end)
  (interactive "P\nr")
  (jprop-convert-region-to rev start end t))
(defun jprop-convert-buffer (rev)
  (interactive "P")
  (jprop-convert-region rev (point-min) (point-max)))

(add-hook 'conf-javaprop-mode-hook
          (lambda ()
            (let ((m (copy-keymap jprop-mode-map)))
              (set-keymap-parent m conf-javaprop-mode-map)
              (use-local-map m))))

(provide 'jprop)
2011/04/06 / highmt

オブジェクトパッセージ図 Ver.0.1.0

処理がどう進んでいくのかを分析するのに、コールグラフやコミュニケーション
図、シーケンス図、といったものをよく使うと思います。

しかし、これらの図は、基本的には呼び出しの関係を表すものであるため、特に
オブジェクトをつくってわたして、といったことを行うような処理では、オブジェ
クト間の関係を表すのにはがゆい思いをしたりします。

そこで、試験的にオブジェクトパッセージ図なるものを書いてみました。
以下のようなことがらを表すのを目標にしています:

  • オブジェクトaからオブジェクトbのm1を呼び出す
  • オブジェクトaからオブジェクトbのm1を呼び出してオブジェクトcを渡す
  • オブジェクトaからオブジェクトbのm1を呼び出してオブジェクトcを生成する

試みとして、Android の Service の使用方法をオブジェクトパッセージ図にし
てみました。
(この辺の内容のつもりですが間違ってるかもしれません。→http://developer.android.com/guide/developing/tools/aidl.html)
あまりよくできてないところもあるので、とりあえずVer.0.1.0として公開してみ
ます。

図の要素の説明等は今は雰囲気だけでいずれまた整理したいと思います。

オブジェクトパッセージ図

ソース(graphviz)

digraph sampleservice {
  graph [nodesep="1.2", ordering="in"];
  node [fontname="tahoma", fontsize="8"];
  edge [fontname="tahoma", fontsize="8", arrowsize="0.5"];

  AIDL [label="<<aidl>>\nIXxxService"];
  AIDL -> IXxxService_Stub [label="<<generate>>"];
  AIDL -> IXxxService [label="<<generate>>"];

  Binder;
  IXxxService_Stub [shape=Mrecord,label="<x>IXxxService.Stub|{<m1>doXxx|<m2>asInterface}"];
  Binder -> IXxxService_Stub [dir=back, arrowtail=empty, arrowsize="1.5"];

  XxxService_Stub [shape=Mrecord,label="<x>\<\<impl\>\>\nXxxService.Stub|{<m1>\<\<override\>\>\ndoXxx}"];
  IXxxService_Stub -> XxxService_Stub [dir=back, arrowtail=empty, arrowsize="1.5"];

  Service;
  XxxService [shape=Mrecord,label="<x>XxxService|{<m1>\<\<\override\>\>\nonBind}"];
  Service -> XxxService [dir=back, arrowtail=empty, arrowsize="1.5"];

  XxxServiceProxy [shape=Mrecord,label="<x>XxxServiceProxy(internal)|{<m1>\<\<\override\>\>\ndoXxx}"];
  IXxxService_Stub:m2 -> XxxServiceProxy [label="(8)\n<<lead to>>", style="dashed"];
  XxxService:m1 -> XxxService_Stub:x [label="(5)\n<<lead to>>", style="dashed"];

  Activity;
  XxxActivity [shape=Mrecord, label="<x>XxxActivity|{<c0>ctor()|<m1>\<\<\override\>\>\nonCreate|<m2>\<\<\override\>\>\nonDestroy|<m5>onKey|<m3>\<\<inherit\>\>\nbindService|<m4>\<\<inherit\>\>\nunbindService}"];
  Activity -> XxxActivity [dir=back, arrowtail=empty, arrowsize="1.5"];

  ServiceConnection;
  XxxServiceConnection [shape=Mrecord, label="<x>XxxServiceConnection|{<m1>\<\<override\>\>\nonServiceConnected|<m2>\<\<override\>\>\nonServiceDisconnected}"];
  ServiceConnection -> XxxServiceConnection [dir=back, arrowtail=empty, arrowsize="1.5"];
  
  XxxActivity:c0 -> XxxServiceConnection [label="(1)\n<<create>>"];
  
  Intent1 [label="Intent\n{ISampleService.class}"];
  XxxActivity:m1 -> Intent1 [label="(2)\n<<create>>"];
  XxxActivity:m1 -> XxxActivity:m3  [taillabel="(3)", labeldistance="6" labelangle="-20"];
  XxxActivity:m3 -> Android [label="(4)"];
  Intent1 -> XxxActivity:m3 [label="(3)\n<<passed to>>", style=dotted];
  XxxServiceConnection -> XxxActivity:m3 [label="(3)\n<<passed to>>", style=dotted];

  XxxActivity:m5 -> XxxServiceProxy:m1 [label="(10)"];
  
  XxxServiceConnection -> XxxActivity:m4 [label="(11)\n<<passed to>>", style=dotted];

  Android -> XxxService:m1 [label="(5)"];
  
  Android -> XxxServiceConnection:m1 [label="(7)"];
  XxxService_Stub -> XxxServiceConnection:m1 [label="(7)\n<<passed to>>", style=dotted];
  
  XxxServiceConnection:m1 -> IXxxService_Stub:m2 [label="(8)"];
  
  XxxActivity:m2 -> XxxActivity:m4 [taillabel="(11)", labeldistance="6", labelangle="-20"];
  XxxActivity:m4 -> Android [label="(12)"];
  Android -> XxxServiceConnection:m2 [label="(13)"];
}
2011/03/17 / highmt

javaのプロパティファイルを比較するWinMergeプラグイン

svnでどういう変更が入ったのか追いかけるのに不便だったのでつくってみた。
どこかにありそうなものだけど…

 
準備:

  1. JDKをインストールする
  2. 環境変数 JAVA_HOME をJDKのインストールディレクトリに設定(オプショナル)

 
設定:

  1. 以下を jprop.sct という名前で保存する:
    
    <scriptlet>
        <implements type="Automation" id="dispatcher">
            <property name="PluginEvent"><get/></property>
            <property name="PluginDescription"><get/></property>
            <property name="PluginFileFilters"><get/></property>
            <property name="PluginIsAutomatic"><get/></property>
            <method   name="UnpackFile"/>
            <method   name="PackFile"/>
        </implements>
        <script language="VBS">
    
    Option Explicit
    
    Const defaultJavaHome = "C:\jdk" '←ここは自分の環境にに応じて変更
    
    Function get_PluginEvent()
        get_PluginEvent = "FILE_PACK_UNPACK"
    End Function
    
    Function get_PluginDescription()
        get_PluginDescription = "Convert Java Propertis Files"
    End Function
    
    Function get_PluginFileFilters()
        get_PluginFileFilters = "\.properties$"
    End Function
    
    Function get_PluginIsAutomatic()
        get_PluginIsAutomatic = True
    End Function
    
    Function UnpackFile(fileSrc, fileDst, pbChanged, pSubcode)
        Dim WshShell
        Dim javaHome
        Dim objExec
    
        Set WshShell = CreateObject("WScript.Shell")
        javaHome = WshShell.Environment("Process")("JAVA_HOME")
        If javaHome = "" Then
            javaHome = defaultJavaHome
        End If
        Set objExec = WshShell.Exec(javaHome + "\bin\native2ascii -reverse """ + fileSrc + """ """ + fileDst + """" )
        Do While objExec.Status = 0
            'WScript.Sleep(100)
        Loop
    
        pbChanged = True
        pSubcode = 0
    
        UnpackFile = True
    End Function
    
    Function PackFile(fileSrc, fileDst, pbChanged, pSubcode)
        Dim WshShell
        Dim javaHome
        Dim objExec
    
        Set WshShell = CreateObject("WScript.Shell")
        javaHome = WshShell.Environment("Process")("JAVA_HOME")
        If javaHome = "" Then
            javaHome = defaultJavaHome
        End If
        Set objExec = WshShell.Exec(javaHome + "\bin\native2ascii """ + fileSrc + """ """ + fileDst + """" )
        Do While objExec.Status = 0
            'WScript.Sleep(100)
        Loop
    
        pbChanged = True
        pSubcode = 0
    
        PackFile = True
    End Function
    
        </script>
    </scriptlet>
    
    
  2. jprop.sctを WinMerge をインストールしたディレクトリの下の
    MergePlugins ディレクトリに置く

 

使い方:

  1. WinMergeで比較する
  2. [プラグイン]-[展開プラグインで開く]を選び、
    [展開プラグインの選択]ダイアログの[ファイル展開プラグイン]で
    jprop.sctを選ぶ

 

WScriptオブジェクトにアクセスできないので
ポーリング中Sleepしてないのが難点ですが、
最近のマルチコアなPCならさほど影響はないでしょうきっと…
もしくは、ExecじゃなくてRunとか使えばいいのかもしれませんが…

2011/03/11 / highmt

Androidでのgdbでのデバッグ

環境:

  • Windows
  • cygwin 1.7.8
  • Android SDK r10
  • Android NDK r5b

 
前提:

  • 環境変数NDK_ROOTを設定済であること。
  • Android SDKにパスを通してあること。
  • ndk-buildでビルド済でプロジェクトフォルダのlibsにgdbserverがあること。
    できれば-gでビルド済であること。
  • apkがdebuggableであること。
  • 端末/エミュレータが起動済であること。
  • apkが端末/エミュレータにインストール済で、プロセスが起動済であること。
  • 端末/エミュレータのシェルにrootで入れること。(Android 2.1以前の場合)

 
A. Android 2.2 以上の場合

  • cygwinのシェルでプロジェクトのルートフォルダ上で$NDK_ROOT/ndk-gdbを実行。
  • gdbが起動するのであとは煮るなり焼くなり。

 
B. Android 2.1 以前の場合

  • 以下のスクリプトをプロジェクトのルートフォルダにmy-ndk-gdbの名前で保存。
    #!/bin/sh
    
    ANDROID_NDK_ROOT=$NDK_ROOT
    . $NDK_ROOT/build/core/ndk-common.sh
    
    force_32bit_binaries
    find_program ADB_CMD adb
    ADB_FLAGS=
    AWK_CMD=awk
    DEBUG_PORT=5039
    
    # Delay in seconds between launching the activity and attaching gdbserver on it.
    # This is needed because there is no way to know when the activity has really
    # started, and sometimes this takes a few seconds.
    DELAY=2
    
    PARAMETERS=
    OPTION_HELP=no
    OPTION_PROJECT=
    OPTION_FORCE=no
    OPTION_ADB=
    OPTION_EXEC=
    OPTION_START=no
    OPTION_LAUNCH=
    OPTION_LAUNCH_LIST=no
    OPTION_DELAY=
    
    check_parameter ()
    {
        if [ -z "$2" ]; then
            echo "ERROR: Missing parameter after option '$1'"
            exit 1
        fi
    }
    
    check_adb_flags ()
    {
        if [ -n "$ADB_FLAGS" ] ; then
            echo "ERROR: Only one of -e, -d or -s  can be used at the same time!"
            exit 1
        fi
    }
    
    get_build_var ()
    {
        if [ -z "$GNUMAKE" ] ; then
            GNUMAKE=make
        fi
        $GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1
    }
    
    get_build_var_for_abi ()
    {
        if [ -z "$GNUMAKE" ] ; then
            GNUMAKE=make
        fi
        $GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1 APP_ABI=$2
    }
    
    # Used to run an awk script on the manifest
    run_awk_manifest_script ()
    {
        $AWK_CMD -f $AWK_SCRIPTS/$1 $PROJECT/$MANIFEST
    }
    
    if [ "$HOST_OS" = "cygwin" ] ; then
    # Return native path representation from cygwin one
    # $1: a cygwin-compatible path (e.g. /cygdrive/c/some/thing)
    # Return: path in host windows representation, e.g. C:/some/thing
    #
    # We use mixed mode (i.e. / as the directory separator) because
    # all the tools we use recognize it properly, and it avoids lots
    # of escaping nonsense associated with "\"
    #
    native_path ()
    {
        cygpath -m $1
    }
    else # HOST_OS != windows
    native_path ()
    {
        echo "$1"
    }
    fi # HOST_OS != windows
    
    VERBOSE=no
    while [ -n "$1" ]; do
        opt="$1"
        optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
        case "$opt" in
            --help|-h|-\?)
                OPTION_HELP=yes
                ;;
            --verbose)
                VERBOSE=yes
                ;;
            -s)
                check_parameter $1 $2
                check_adb_flags
                ADB_FLAGS=" -s $2"
                shift
                ;;
            -s*)
                check_adb_flags
                optarg=`expr -- "$opt" : '-s\(.*\)'`
                ADB_FLAGS=" -s $optarg"
                ;;
            -p)
                check_parameter $1 $2
                OPTION_PROJECT="$2"
                shift
                ;;
            -p*)
                optarg=`expr -- "$opt" : '-p\(.*\)'`
                OPTION_PROJECT="$optarg"
                ;;
            --exec=*)
                OPTION_EXEC="$optarg"
                ;;
            -x)
                check_parameter $1 $2
                OPTION_EXEC="$2"
                shift
                ;;
            -x*)
                optarg=`expr -- "$opt" : '-x\(.*\)'`
                OPTION_EXEC="$optarg"
                ;;
            -e)
                check_adb_flags
                ADB_FLAGS=" -e"
                ;;
            -d)
                check_adb_flags
                ADB_FLAGS=" -d"
                ;;
            --adb=*) # specify ADB command
                OPTION_ADB="$optarg"
                ;;
            --awk=*)
                AWK_CMD="$optarg"
                ;;
            --project=*)
                OPTION_PROJECT="$optarg"
                ;;
            --port=*)
                DEBUG_PORT="$optarg"
                ;;
            --force)
                OPTION_FORCE="yes"
                ;;
            --launch-list)
                OPTION_LAUNCH_LIST="yes"
                ;;
            --launch=*)
                OPTION_LAUNCH="$optarg"
                ;;
            --start)
                OPTION_START=yes
                ;;
            --delay=*)
                OPTION_DELAY="$optarg"
                ;;
            -*) # unknown options
                echo "ERROR: Unknown option '$opt', use --help for list of valid ones."
                exit 1
            ;;
            *)  # Simply record parameter
                if [ -z "$PARAMETERS" ] ; then
                    PARAMETERS="$opt"
                else
                    PARAMETERS="$PARAMETERS $opt"
                fi
                ;;
        esac
        shift
    done
    
    if [ "$OPTION_HELP" = "yes" ] ; then
        echo "Usage: $PROGNAME [options]"
        echo ""
        echo "Setup a gdb debugging session for your Android NDK application."
        echo "Read $$NDK/docs/NDK-GDB.TXT for complete usage instructions."
        echo ""
        echo "Valid options:"
        echo ""
        echo "    --help|-h|-?      Print this help"
        echo "    --verbose         Enable verbose mode"
        echo "    --force           Kill existing debug session if it exists"
        echo "    --start           Launch application instead of attaching to existing one"
        echo "    --launch=   Same as --start, but specify activity name (see below)"
        echo "    --launch-list     List all launchable activity names from manifest"
        echo "    --delay=    Delay in seconds between activity start and gdbserver attach."
        echo "    --project=  Specify application project path"
        echo "    -p          Same as --project="
        echo "    --port=     Use tcp:localhost: to communicate with gdbserver [$DEBUG_PORT]"
        echo "    --exec=     Execute gdb initialization commands in  after connection"
        echo "    -x          Same as --exec="
        echo "    --adb=      Use specific adb command [$ADB_CMD]"
        echo "    --awk=      Use specific awk command [$AWK_CMD]"
        echo "    -e                Connect to single emulator instance"
        echo "    -d                Connect to single target device"
        echo "    -s        Connect to specific emulator or device"
        echo ""
        exit 0
    fi
    
    log "Android NDK installation path: $ANDROID_NDK_ROOT"
    
    if [ -n "$OPTION_EXEC" ] ; then
        if [ ! -f "$OPTION_EXEC" ]; then
            echo "ERROR: Invalid initialization file: $OPTION_EXEC"
            exit 1
        fi
    fi
    
    if [ -n "$OPTION_DELAY" ] ; then
        DELAY="$OPTION_DELAY"
    fi
    
    # Check ADB tool version
    if [ -n "$OPTION_ADB" ] ; then
        ADB_CMD="$OPTION_ADB"
        log "Using specific adb command: $ADB_CMD"
    else
        if [ -z "$ADB_CMD" ] ; then
            echo "ERROR: The 'adb' tool is not in your path."
            echo "       You can change your PATH variable, or use"
            echo "       --adb= to point to a valid one."
            exit 1
        fi
        log "Using default adb command: $ADB_CMD"
    fi
    
    ADB_VERSION=`$ADB_CMD version`
    if [ $? != 0 ] ; then
        echo "ERROR: Could not run ADB with: $ADB_CMD"
        exit 1
    fi
    log "ADB version found: $ADB_VERSION"
    
    ADB_CMD="${ADB_CMD}${ADB_FLAGS}"
    log "Using final ADB command: '$ADB_CMD'"
    
    adb_shell ()
    {
        # Run an adb shell command and return its output.
        #
        # We need to filter weird control characters like \r that are
        # included in the output.
        #
        $ADB_CMD shell $@ | sed -e 's![[:cntrl:]]!!g'
    }
    
    # Check the awk tool
    AWK_SCRIPTS=$ANDROID_NDK_ROOT/build/awk
    AWK_TEST=`$AWK_CMD -f $AWK_SCRIPTS/check-awk.awk`
    if [ $? != 0 ] ; then
        echo "ERROR: Could not run '$AWK_CMD' command. Do you have it installed properly?"
        exit 1
    fi
    if [ "$AWK_TEST" != "Pass" ] ; then
        echo "ERROR: Your version of 'awk' is obsolete. Please use --awk= to point to Nawk or Gawk!"
        exit 1
    fi
    
    # Name of the manifest file
    MANIFEST=AndroidManifest.xml
    
    # Find the root of the application project.
    if [ -n "$OPTION_PROJECT" ] ; then
        PROJECT=$OPTION_PROJECT
        log "Using specified project path: $PROJECT"
        if [ ! -d "$PROJECT" ] ; then
            echo "ERROR: Your --project option does not point to a directory!"
            exit 1
        fi
        if [ ! -f "$PROJECT/$MANIFEST" ] ; then
            echo "ERROR: Your --project does not point to an Android project path!"
            echo "       It is missing a $MANIFEST file."
            exit 1
        fi
    else
        # Assume we are in the project directory
        if [ -f "$MANIFEST" ] ; then
            PROJECT=.
        else
            PROJECT=
            CURDIR=`pwd`
            while [ "$CURDIR" != "/" ] ; do
                if [ -f "$CURDIR/$MANIFEST" ] ; then
                    PROJECT="$CURDIR"
                    break
                fi
                CURDIR=`dirname $CURDIR`
            done
            if [ -z "$PROJECT" ] ; then
                echo "ERROR: Launch this script from an application project directory, or use --project=."
                exit 1
            fi
        fi
        log "Using auto-detected project path: $PROJECT"
    fi
    
    # Extract the package name from the manifest
    PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk`
    log "Found package name: $PACKAGE_NAME"
    if [ $? != 0 -o "$PACKAGE_NAME" = "" ] ; then
        echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST."
        echo "       Please check that the file is well-formed!"
        exit 1
    fi
    
    # If --launch-list is used, list all launchable activities, and be done with it
    if [ "$OPTION_LAUNCH_LIST" = "yes" ] ; then
        log "Extracting list of launchable activities from manifest:"
        run_awk_manifest_script extract-launchable.awk
        exit 0
    fi
    
    APP_ABIS=`get_build_var APP_ABI`
    log "ABIs targetted by application: $APP_ABIS"
    
    # Check the ADB command, and that we can connect to the device/emulator
    ADB_TEST=`$ADB_CMD shell ls`
    if [ $? != 0 ] ; then
        echo "ERROR: Could not connect to device or emulator!"
        echo "       Please check that an emulator is running or a device is connected"
        echo "       through USB to this machine. You can use -e, -d and -s "
        echo "       in case of multiple ones."
        exit 1
    fi
    
    # Get the target device's supported ABI(s)
    # And check that they are supported by the application
    #
    COMPAT_ABI=none
    CPU_ABI=`adb_shell getprop ro.product.cpu.abi`
    for ABI in $APP_ABIS; do
        if [ "$ABI" = "$CPU_ABI" ] ; then
            COMPAT_ABI=$CPU_ABI
            break
        fi
    done
    
    CPU_ABI2=`adb_shell getprop ro.product.cpu.abi2`
    if [ -z "$CPU_ABI2" ] ; then
        log "Device CPU ABI: $CPU_ABI"
    else
        log "Device CPU ABIs: $CPU_ABI $CPU_ABI2"
        if [ "$COMPAT_ABI" = "none" ] ; then
            for ABI in $APP_ABIS; do
                if [ "$ABI" = "$CPU_ABI2" ] ; then
                    COMPAT_ABI=$CPU_ABI2
                    break
                fi
            done
        fi
    fi
    if [ "$COMPAT_ABI" = none ] ; then
        echo "ERROR: The device does not support the application's targetted CPU ABIs!"
        if [ "$CPU_ABI2" = "$CPU_ABI" ] ; then
            CPU_ABI2=
        fi
        echo "       Device supports:  $CPU_ABI $CPU_ABI2"
        echo "       Package supports: $APP_ABIS"
        exit 1
    fi
    log "Compatible device ABI: $COMPAT_ABI"
    
    # Check that the application is debuggable, or nothing will work
    DEBUGGABLE=`run_awk_manifest_script extract-debuggable.awk`
    log "Found debuggable flag: $DEBUGGABLE"
    if [ $? != 0 -o "$DEBUGGABLE" != "true" ] ; then
        # If gdbserver exists, then we built with 'ndk-build NDK_DEBUG=1' and it's
        # ok to not have android:debuggable set to true in the original manifest.
        # However, if this is not the case, then complain!!
        if [ -f $PROJECT/libs/$COMPAT_ABI/gdbserver ] ; then
            log "Found gdbserver under libs/$COMPAT_ABI, assuming app was built with NDK_DEBUG=1"
        else
            echo "ERROR: Package $PACKAGE_NAME is not debuggable ! You can fix that in two ways:"
            echo ""
            echo "  - Rebuilt with the NDK_DEBUG=1 option when calling 'ndk-build'."
            echo ""
            echo "  - Modify your manifest to set android:debuggable attribute to \"true\","
            echo "    then rebuild normally."
            echo ""
            echo "After one of these, re-install to the device!"
            exit 1
        fi
    else
        # DEBUGGABLE is true in the manifest. Let's check that the user didn't change the
        # debuggable flag in the manifest without calling ndk-build afterwards.
        if [ ! -f $PROJECT/libs/$COMPAT_ABI/gdbserver ] ; then
            echo "ERROR: Could not find gdbserver binary under $PROJECT/libs/$COMPAT_ABI"
            echo "       This usually means you modified your AndroidManifest.xml to set"
            echo "       the android:debuggable flag to 'true' but did not rebuild the"
            echo "       native binaries. Please call 'ndk-build' to do so,"
            echo "       *then* re-install to the device!"
            exit 1
        fi
    fi
    
    # push the gdbserver to  of the package on the device
    APP_DST=`get_build_var NDK_APP_DST_DIR`
    log "Using app dst directory: $APP_DST"
    run $ADB_CMD push `native_path $APP_DST/gdbserver` /data/data/$PACKAGE_NAME/lib/gdbserver
    run $ADB_CMD shell chmod 777 /data/data/$PACKAGE_NAME/lib/gdbserver
    
    # Let's check that 'gdbserver' is properly installed on the device too. If this
    # is not the case, the user didn't install the proper package after rebuilding.
    #
    DEVICE_GDBSERVER=`adb_shell ls /data/data/$PACKAGE_NAME/lib/gdbserver`
    log "Found device gdbserver: $DEVICE_GDBSERVER"
    if pattern_match "No such file or directory" "$DEVICE_GDBSERVER" ] ; then
        echo "ERROR: Non-debuggable application installed on the target device."
        echo "       Please re-install the debuggable version!"
        exit 1
    fi
    
    # Get information from the build system
    GDBSETUP_INIT=`get_build_var_for_abi NDK_APP_GDBSETUP $COMPAT_ABI`
    log "Using gdb setup init: $GDBSETUP_INIT"
    
    TOOLCHAIN_PREFIX=`get_build_var_for_abi TOOLCHAIN_PREFIX $COMPAT_ABI`
    log "Using toolchain prefix: $TOOLCHAIN_PREFIX"
    
    APP_OUT=`get_build_var_for_abi TARGET_OUT $COMPAT_ABI`
    log "Using app out directory: $APP_OUT"
    
    # Find the  of the package on the device
    DATA_DIR=`adb_shell /system/bin/sh -c "cd /data/data/$PACKAGE_NAME/lib && pwd"`
    log "Found data directory: '$DATA_DIR'"
    if [ $? != 0 -o -z "$DATA_DIR" ] ; then
        echo "ERROR: Could not extract package's data directory. Are you sure that"
        echo "       your installed application is debuggable?"
        exit 1
    fi
    
    # Launch the activity if needed
    if [ "$OPTION_START" = "yes" ] ; then
        # If --launch is used, ignore --start, otherwise extract the first
        # launchable activity name from the manifest and use it as if --launch=
        # was used instead.
        #
        if [ -z "$OPTION_LAUNCH" ] ; then
            OPTION_LAUNCH=`run_awk_manifest_script extract-launchable.awk | sed 2q`
            if [ $? != 0 ] ; then
                echo "ERROR: Could not extract name of launchable activity from manifest!"
                echo "       Try to use --launch= directly instead as a work-around."
                exit 1
            fi
            log "Found first launchable activity: $OPTION_LAUNCH"
            if [ -z "$OPTION_LAUNCH" ] ; then
                echo "ERROR: It seems that your Application does not have any launchable activity!"
                echo "       Please fix your manifest file and rebuild/re-install your application."
                exit 1
            fi
        fi
    fi
    
    if [ -n "$OPTION_LAUNCH" ] ; then
        log "Launching activity: $PACKAGE_NAME/$OPTION_LAUNCH"
        run $ADB_CMD shell am start -n $PACKAGE_NAME/$OPTION_LAUNCH
        if [ $? != 0 ] ; then
            echo "ERROR: Could not launch specified activity: $OPTION_LAUNCH"
            echo "       Use --launch-list to dump a list of valid values."
            exit 1
        fi
        # Sleep a bit, it sometimes take one second to start properly
        # Note that we use the 'sleep' command on the device here.
        run $ADB_CMD shell sleep $DELAY
    fi
    
    # Find the PID of the application being run
    PID=`$ADB_CMD shell ps | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE=$PACKAGE_NAME`
    log "Found running PID: $PID"
    if [ $? != 0 -o "$PID" = "0" ] ; then
        echo "ERROR: Could not extract PID of application on device/emulator."
        if [ -n "$OPTION_LAUNCH" ] ; then
            echo "       Weird, this probably means one of these:"
            echo ""
            echo "         - The installed package does not match your current manifest."
            echo "         - The application process was terminated."
            echo ""
            echo "       Try using the --verbose option and look at its output for details."
        else
            echo "       Are you sure the application is already started?"
            echo "       Consider using --start or --launch= if not."
        fi
        exit 1
    fi
    
    # Check that there is no other instance of gdbserver running
    GDBSERVER_PS=`$ADB_CMD shell ps | grep lib/gdbserver`
    if [ -n "$GDBSERVER_PS" ] ; then
        if [ "$OPTION_FORCE" = "no" ] ; then
            echo "ERROR: Another debug session running, Use --force to kill it."
            exit 1
        fi
        log "Killing existing debugging session"
        GDBSERVER_PID=`echo $GDBSERVER_PS | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE=lib/gdbserver`
        if [ $GDBSERVER_PID != 0 ] ; then
            run $ADB_CMD shell kill -9 $GDBSERVER_PID
        fi
    fi
    
    # Launch gdbserver now
    DEBUG_SOCKET=debug-socket
    run $ADB_CMD shell /data/data/$PACKAGE_NAME/lib/gdbserver 0:$DEBUG_PORT --attach $PID &
    if [ $? != 0 ] ; then
        echo "ERROR: Could not launch gdbserver on the device?"
        exit 1
    fi
    log "Launched gdbserver succesfully."
    
    # Setup network redirection
    log "Setup network redirection"
    run $ADB_CMD forward tcp:$DEBUG_PORT tcp:$DEBUG_PORT
    if [ $? != 0 ] ; then
        echo "ERROR: Could not setup network redirection to gdbserver?"
        echo "       Maybe using --port= to use a different TCP port might help?"
        exit 1
    fi
    
    # Get the app_server binary from the device
    APP_PROCESS=$APP_OUT/app_process
    run $ADB_CMD pull /system/bin/app_process `native_path $APP_PROCESS`
    log "Pulled app_process from device/emulator."
    
    run $ADB_CMD pull /system/lib/libc.so `native_path $APP_OUT/libc.so`
    log "Pulled libc.so from device/emulator."
    
    # Now launch the appropriate gdb client with the right init commands
    #
    GDBCLIENT=${TOOLCHAIN_PREFIX}gdb
    GDBSETUP=$APP_OUT/gdb.setup
    cp -f $GDBSETUP_INIT $GDBSETUP
    #uncomment the following to debug the remote connection only
    #echo "set debug remote 1" >> $GDBSETUP
    echo "file `native_path $APP_PROCESS`" >> $GDBSETUP
    echo "target remote :$DEBUG_PORT" >> $GDBSETUP
    if [ -n "$OPTION_EXEC" ] ; then
        cat $OPTION_EXEC >> $GDBSETUP
    fi
    $GDBCLIENT -x `native_path $GDBSETUP`

    上記はndk-gdbを編集したもの。オリジナルのファイルには以下のライセンス表
    記が入っています。(っていう書き方でライセンス条件満たしているのでしたっ
    けか。)

    #
    # Copyright (C) 2010 The Android Open Source Project
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #      http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    #
  • cygwinのシェルでプロジェクトのルートフォルダ上で./my-ndk-gdb を実行。
  • gdbが起動するのであとは煮るなり焼くなり。

 
Android 2.1以前の場合のは、巷の情報をもとにndk-gdbを編集したもの。
ndk-gdbがもとになっているので上記以外の方法でもできるはず。

——–
2011/03/18 00:06 (JST): 「nkd」とかいてたところがあったので「ndk」に修正。

2011/02/07 / highmt

Adobe Reader X の検索ボックスでskkimeを使用する方法

以下のいずれかを実施すればOKっぽい。
セキュリティレベルを弱めるので自己責任でお願いします。

  • 方法1
    [編集]-[環境設定]-[一般]の[起動時に保護モードを有効にする]をoffにする
  • 方法2
    1. HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Adobe\Acrobat Reader\10.0\FeatureLockDown

      DWORD値 bUseWhitelistConfigFile をつくり 1を設定する
    2. C:\Program Files\Adobe\Reader 10.0\Reader
      (もしくは C:\Program Files (x86)\Adobe\Reader 10.0\Reader など、AcroRd32.exeのインストールされている場所)に
      テキストファイル ProtectedModeWhitelistConfig.txt をつくり
      中に 

        FILES_ALLOW_ANY=\??\pipe\skkiserv010500_pipe_*

      を書く。
      (*はワイルドカード。skkimeは*のところをユーザ名にした名前のパイプをつくるので、ユーザ名にしておいたほうが安全かも。)

参考:http://kb2.adobe.com/jp/cps/880/cpsid_88069.html

2011/01/22 / highmt

uim-fepでAquaSKKのskkservを使う

Darwin mbp-090704.local 10.6.0 Darwin Kernel Version 10.6.0
uim-fep version 1.6.1

uim-fepを使うとターミナルでも ^J でSKKを使えるようになります。
AquaSKKでも(TSM版では)かなキーで日本語モードに入れますが、
つい^Jを押してしまうことがあるので便利です。
また、vncではだいたいかなキーが通らないので便利です。

  • AquaSKKのskkservを有効にする
  • ソースをダウンロードする:
    http://code.google.com/p/uim/downloads/list
  • アーカイブを展開する
  • ./configure
  • make
  • sudo make install
  • uim-pref-gtk
    X11が起動するまで少し時間がかかる
    Xlib: extension “RANDR” missing on display とか出ても気にしない
    SKK dictionaries グループ の SKK Server あたりを変更する
  • uim-fep

ですが、ある読みで一回変換すると別の読みで変換できなくなるという不具合が
ありました。
skkservを使わないという選択肢もありますが、やっぱり辞書をわけるのも嫌な感じなので
uim-fep側に以下のパッチを適用。
AquaSKK用なので他のskkservでは逆にだめになるかもしれません。

--- uim/skk.c.orig	2011-01-07 11:17:32.000000000 +0900
+++ uim/skk.c	2011-01-22 10:16:06.000000000 +0900
@@ -797,7 +797,8 @@
 
   uim_asprintf(&idx, "%s%c", s, okuri_head);
 
-  fprintf(wserv, "1%s \n", idx);
+  //  fprintf(wserv, "1%s \n", idx);
+  fprintf(wserv, "1%s ", idx);
   ret = fflush(wserv);
   if (ret != 0 && errno == EPIPE) {
     free(idx);
@@ -847,6 +848,8 @@
     while ((nr = read(skkservsock, &r, 1)) != -1 && nr != 0 && r != '\n')
       ;
     free(line);
+    // disconnect to re-sync
+    skkserv_disconnected(di);
     return NULL;
   }
 }