2008년 10월 21일 (화)

이맥스 프레임 빨리 바꾸기

빠른 답변: other-frame 함수가 C-x 5 o로 바인딩되어 있습니다만, 그것에 대하여 얘기하고자 하는 것은 아닙니다.

저는 코드 작성할 때 키보드에서 다른 곳으로 집중을 빼앗기는 것을 싫어합니다. 전, 키보드와 마우스를 넘나들면서도 남들보다 빨리 코딩할 자신이 없습니다.

그래서 될 수 있으면 마우스를 안 쓰려고 하는데, 그건 쥐들이 싫어서 그런 것이 아니라 키보드로 코드 쓰는 것이 제일 편하기 때문입니다. 키보드 없이 코딩하는 것을 좋아하시는 분이 있을지도 모르겠습니다. (그치만, 음성 인식 시스템으로 펄 코딩하는 비디오 보셨나요?)

어쨌든 저는 그놈 작업 공간 하나에 이맥스 프레임을 6개 올려 놓고 사용합니다. 이맥스 프레임이라는 것이 X 상에서는 창이지요. 그리고 알트 탭 신공으로는 창 바꾸기가 너무 힘듭니다. 전 알트 탭 신공에 능하지 않아서 알트를 누르고 있으면서 탭을 몇 번 눌러야 제가 원하는 창으로 갈 수 있는지를 모릅니다. 어떤 분은 한번에 10개 이상 창을 띄워 놓고 작업하시더라구요.

웹에 찾아보니까 Joseph Perla라는 분의 방법을 이용할 수 있겠더라구요. 아주 재미있는 방법인데, 제가 창을 옮길 때마다 코드를 수정하고 싶지는 않더라구요.

특정 방향에 있는 프레임으로 옮기면 정말 좋을 것 같다고 생각했죠. X 윈도 시스템을 좀 더 파 볼까 생각했지만, 어차피 화면에 놓여 있는 창들은 모두 이맥스 프레임인데, ELisp으로 짜는 것이 더 낫고, 더 재미있겠다고 생각했습니다.

그걸 하기 위한 이맥스 코드를 작성했습니다. 슈퍼키+왼쪽 화살표로 왼쪽에 있는 가장 가까운 이맥스 프레임(창)으로 이동합니다. 다른 화살표도 마찬가지.

ELisp 코드

아래 코드를 복사해서 붙인 다음 ELisp 코드를 두는 곳에 저장하세요.

;; directed-switch-to-frame.el -- Switching to frames with direction.

;; Author: Jaehyun Yeom
;; Keywords: frame, direction

;; Move 4 direction to switch emacs frames.

(defun cartesian-to-polar (cartesian)
  "Convert cartesian coordinate to polar coordinate."
  (let ((x (float (car cartesian))) (y (float (cdr cartesian))))
    (cons
     (sqrt (+ (* x x) (* y y)))
     (cond ((and (= x 0) (> y 0)) (* pi 0.5))
           ((and (= x 0) (< y 0)) (* pi 1.5))
           ((< x 0) (+ (atan (/ y x)) pi))
           ((and (> x 0) (< y 0)) (+ (atan (/ y x)) (* pi 2)))
           ((> x 0) (atan (/ y x)))
           (t nil)))))

(defun switch-to-frame-with-direction (dir-in-deg var-in-deg next)
  "Switch to frame with direction with variation."
  (let ((f (sort (delq nil
                       (let ((list-of-frames (mapcar (lambda (each-frame)
                                                       (cons each-frame
                                                             (cons (+ (frame-parameter each-frame 'left)
                                                                      (/ (frame-pixel-width each-frame) 2))
                                                                   (+ (frame-parameter each-frame 'top)
                                                                      (/ (frame-pixel-height each-frame) 2)))))
                                                     (cons (selected-frame) 
                                                           (delete-if (lambda (item)
                                                                        (or (eq (selected-frame) item)
                                                                            (eq nil (frame-parameter item 'window-id))))
                                                                        (visible-frame-list)))))
                             (dir-in-rad (degrees-to-radians (mod dir-in-deg 360)))
                             (var-in-rad (degrees-to-radians (mod var-in-deg 360))))
                         (mapcar
                          (lambda (each-frame)
                            (let ((polar (cartesian-to-polar (cons (- (cadr each-frame) (cadr (car list-of-frames)))
                                                                   (- (cddr each-frame) (cddr (car list-of-frames)))))))
                              (when (or (not (numberp (cdr polar)))
                                        (< (- (* 2 pi) (abs (- (cdr polar) dir-in-rad))) var-in-rad)
                                        (< (abs (- (cdr polar) dir-in-rad)) var-in-rad))
                                (cons (car each-frame) polar))))
                          (cdr list-of-frames))))
                 (lambda (a b)
                   (or (not (numberp (cddr b)))
                       (and (numberp (cddr a))
                            (< (cadr a) (cadr b))))))))
    (cond ((not f) nil)
          ((cddar f) (x-focus-frame (caar f)))
          (t (while (not (memq (selected-frame) (mapcar 'car f)))
               (other-frame next))))))

(defun switch-to-right-frame ()
  (interactive)
  (switch-to-frame-with-direction 0 60 -1))

(defun switch-to-down-frame ()
  (interactive)
  (switch-to-frame-with-direction 90 60 -1))

(defun switch-to-left-frame ()
  (interactive)
  (switch-to-frame-with-direction 180 60 1))

(defun switch-to-up-frame ()
  (interactive)
  (switch-to-frame-with-direction 270 60 1))

(provide 'directed-switch-to-frame)

죄송합니다만, 아직 ELisp 코드를 잘 짜는 것은 아닙니다.

키 바인딩

다음 코드를 이맥스 설정 파일인 .emacs에 넣습니다.

;; Moving between frames
(when (load "directed-switch-to-frame")
  (global-set-key [s-left] 'switch-to-left-frame)
  (global-set-key [s-right] 'switch-to-right-frame)
  (global-set-key [s-up] 'switch-to-up-frame)
  (global-set-key [s-down] 'switch-to-down-frame))

되는지 해 볼까요?

이맥스를 새로 시작하고 C-x 5 2를 눌러서 프레임을 여러 개 만듭니다. 보통 슈퍼키는 윈도키로 바인딩 되어 있으므로 윈도키를 누른 상태에서 방향키를 눌러보세요. 잘 되나요?

이맥스 창으로만 움직일 수 있습니다. 그리고 다른 이맥스 인스턴스의 프레임으로는 이동할 수 없습니다.

만약에 프레임의 중심 좌표가 정확하게 겹친 상태에서 프레임이 없는 방향으로 이동하라고 한다면 겹쳐진 창을 순환시킬 수 있습니다. 전체 화면 프레임들을 여러 개 겹쳐 놓고 작업하는 상황에서 유용하게 쓸 수 있습니다. 알트 탭을 누르는 것보다 더 빨리 움직일 수 있습니다. 반대쪽 방향을 누르면 반대로 순환합니다.

Switch Emacs Frames Quickly

Quick answer: Function other-frame is bound to C-x 5 o. But I'm not talking about it.

I don't like to lose focus on my keyboard when I write code. I'm not effective enough to work well while switching between keyboard and mouse.

So I try not to use mouse if possible, not because I hate mice but I can write code easier with keyboard. Someone may prefer coding without keyboard though. (But does anybody watched the video about writing perl code with voice recognition system?)

Anyway, I open about 6 emacs frames–which are windows of X– on a single GNOME workspace. And it's very hard to switch between frames with Alt-Tab skill. I'm not skillful enough with Alt-Tab. I don't know how many Tabs with Alt pressed do I need to switch to my intended frame. I saw someone opens more than 10 frames at a time.

I searched on the net and found Joseph Perla's solution. His solution was very interesting, but I'd rather not change code whenever I move window.

I thought it would be great if I can switch to the frame on some direction. I could research X Window system more, but I chose ELisp rather than that, because all of windows on the screen was Emacs and I enjoyed learning ELisp.

Well, this is ELisp code to do that. I bound super-left key to switch to the nearest frame on the left of current frame, and so on.

ELisp Code

You can copy and paste the following code and save to your ELisp place.

;; directed-switch-to-frame.el -- Switching to frames with direction.

;; Author: Jaehyun Yeom
;; Keywords: frame, direction

;; Move 4 direction to switch emacs frames.

(defun cartesian-to-polar (cartesian)
  "Convert cartesian coordinate to polar coordinate."
  (let ((x (float (car cartesian))) (y (float (cdr cartesian))))
    (cons
     (sqrt (+ (* x x) (* y y)))
     (cond ((and (= x 0) (> y 0)) (* pi 0.5))
           ((and (= x 0) (< y 0)) (* pi 1.5))
           ((< x 0) (+ (atan (/ y x)) pi))
           ((and (> x 0) (< y 0)) (+ (atan (/ y x)) (* pi 2)))
           ((> x 0) (atan (/ y x)))
           (t nil)))))

(defun switch-to-frame-with-direction (dir-in-deg var-in-deg next)
  "Switch to frame with direction with variation."
  (let ((f (sort (delq nil
                       (let ((list-of-frames (mapcar (lambda (each-frame)
                                                       (cons each-frame
                                                             (cons (+ (frame-parameter each-frame 'left)
                                                                      (/ (frame-pixel-width each-frame) 2))
                                                                   (+ (frame-parameter each-frame 'top)
                                                                      (/ (frame-pixel-height each-frame) 2)))))
                                                     (cons (selected-frame) 
                                                           (delete-if (lambda (item)
                                                                        (or (eq (selected-frame) item)
                                                                            (eq nil (frame-parameter item 'window-id))))
                                                                        (visible-frame-list)))))
                             (dir-in-rad (degrees-to-radians (mod dir-in-deg 360)))
                             (var-in-rad (degrees-to-radians (mod var-in-deg 360))))
                         (mapcar
                          (lambda (each-frame)
                            (let ((polar (cartesian-to-polar (cons (- (cadr each-frame) (cadr (car list-of-frames)))
                                                                   (- (cddr each-frame) (cddr (car list-of-frames)))))))
                              (when (or (not (numberp (cdr polar)))
                                        (< (- (* 2 pi) (abs (- (cdr polar) dir-in-rad))) var-in-rad)
                                        (< (abs (- (cdr polar) dir-in-rad)) var-in-rad))
                                (cons (car each-frame) polar))))
                          (cdr list-of-frames))))
                 (lambda (a b)
                   (or (not (numberp (cddr b)))
                       (and (numberp (cddr a))
                            (< (cadr a) (cadr b))))))))
    (cond ((not f) nil)
          ((cddar f) (x-focus-frame (caar f)))
          (t (while (not (memq (selected-frame) (mapcar 'car f)))
               (other-frame next))))))

(defun switch-to-right-frame ()
  (interactive)
  (switch-to-frame-with-direction 0 60 -1))

(defun switch-to-down-frame ()
  (interactive)
  (switch-to-frame-with-direction 90 60 -1))

(defun switch-to-left-frame ()
  (interactive)
  (switch-to-frame-with-direction 180 60 1))

(defun switch-to-up-frame ()
  (interactive)
  (switch-to-frame-with-direction 270 60 1))

(provide 'directed-switch-to-frame)

Sorry, I'm not good at writing ELisp code yet.

Key binding

I put the following code to my .emacs file.

;; Moving between frames
(when (load "directed-switch-to-frame")
  (global-set-key [s-left] 'switch-to-left-frame)
  (global-set-key [s-right] 'switch-to-right-frame)
  (global-set-key [s-up] 'switch-to-up-frame)
  (global-set-key [s-down] 'switch-to-down-frame))

Enjoy

Restart emacs and open multiple frames with C-x 5 2. Usually super key is bound to the Windows key. So try Win-Left, Win-Up, Win-Right, Win-Down. Does it work?

It doesn't move to windows other than Emacs. And it doesn't move to Emacs frame of other emacs instances.

If center coordinate of frames are same, focus cycles if there are no other frames on that direction. This is useful if you opened multiple full screen frames. You can switch more quickly than Alt-Tab. Opposite direction will cycle opposite direction.

2008년 10월 4일 (토)

MULTI TTY 이맥스

이맥스 23 버전에 Multi TTY 기능이 추가될 예정인데, 이에 대하여 소개하고자 한다.

과거: screen을 이용하여 터미널에서 이맥스 공유하기

예전부터 이맥스를 쓰면서 꼭 있었으면 하고 간절히 원했던 기능이 있었다. GUI 이맥스를 터미널 이맥스보다 선호하는데, 어쩔 수 없이 터미널 이맥스를 이용해야 하는 경우가 있다. 그렇지만 GUI 이맥스와 세션을 공유할 수가 없었다.

터미널 이맥스만 이용하면 screen이라는 프로그램으로 즐겁게 이용할 수 있었다. 그 이유는 다음과 같다.

  • screen 실행 후 이맥스를 실행해 두면 다른 머신에서 ssh 등으로 접속을 하더라도 기존의 이맥스를 계속 연결하여 사용할 수 있었다.
  • .screenrc 파일을 적절히 편집하면 screen 프로그램이 새로 구동되면 자동으로 emacs 프로그램을 실행하게 할 수 있어서 편리했다.
  • emacsclient 프로그램을 이용하면 외부 프로그램들이 새로운 이맥스를 실행할 필요 없이 기존 이맥스 프로그램에 해당 파일을 열어달라고 요청할 수 있었다.
  • 쉘 스크립트에서 이맥스가 있는 스크린으로 화면을 전환시킨 후 emacsclient를 실행하게 작성해 두어 이것을 EDITOR 환경변수로 이용하면 해당 파일을 열면서 스크린이 자동 전환되게 할 수 있다.
  • elisp 코드를 약간만 더 작성하면 편집이 끝난 후 C-x # 을 누른 경우에 원래 사용하던 스크린으로 돌아갈 수 있다.

왜 이것이 필요했던지는 새로운 이맥스를 구동하는 것이 별로 좋지 않은 선택이기 때문이다. 이맥스는 구동 시간이 오래 걸리고, 새로운 이맥스는 기존에 실행되고 있던 다른 이맥스와 세션을 공유하지 않는다. 그래서 이맥스 서버라는 것을 동작시키고 클라이언트가 특정 파일을 열어달라고 요청할 수 있게 한 것인데, 일반적으로 이맥스 클라이언트는 사용자가 직접 실행하는 것이 아니라 외부 프로그램이 $EDITOR 환경 변수를 이용하여 자주 사용하는 에디터를 실행시켜 주는 상황에서 주로 사용한다.

터미널이 아닌 경우에 공유할 수 있는 방법

이맥스 자체적으로 Multi TTY를 지원하는 것은 아니었기 때문에 스크린을 통하지 않고 둘 이상의 이맥스의 세션을 공유시킬 방법이 없었다. 스크린을 통하려면 터미널을 이용해야 하는데 터미널 환경에서 이맥스를 이용하는 것은 조금 불편하다. GUI 환경에서만 쓸 수 있는 몇 가지 기능을 이용하지 못하고, 터미널의 한계 때문에 단축키의 제한이 크다. 예를 들어, Shift와 Ctrl을 같이 누르면서 사용해야 하는 단축키들은 터미널에서 동작하지 않는다.

결국, GUI 환경에서 이맥스를 사용하다가 이것을 종료하지 않고, 다른 머신에서 ssh로 접근하여 이맥스 세션을 공유하여 이용할 수 잇으면 좋을 것이다. 물론 X 세션을 공유하여 이것을 처리할 수 있을 것이다. 하지만, 외부에서 X Forwarding 등을 이용하여 ssh 접속을 하는 것은 여러모로 어렵다. 느리기도 하고, 별로 좋은 방법이 아니다.

Multi TTY 이맥스

이런 문제를 해결할 수 있는 것이 바로 Multi TTY 이맥스이다. 어떻게 사용하는지 한 번 해 보자.

  1. 먼저 GUI용 이맥스를 실행한다.
  2. 터미널에서 emacsclient -t 를 입력한다.

두 이맥스가 서로 세션을 공유한다. 같은 버퍼를 보고 있으면 한 곳에서 변화시키면 동시에 두 군데에서 변화되는 것을 볼 수 있다. 이것을 이용하면 다음과 같은 응용이 가능하다.

  1. 주 머신에서는 GUI로 이맥스를 구동하여 사용하다가 참석해야 하는 자리가 있을 때, 참석하여 중간 중간에 노트북으로 접속한 ssh 터미널을 통하여 같은 세션을 그대로 이용할 수 있다.
  2. 회사에서 근무하다가 컴퓨터를 끄지 않고 집에 와서 남은 일들을 마무리 할 수 있다.
  3. IRC 클라이언트인 ERC 등으로 무언가를 질문 했는데, 바로 답이 오지 않았다. 그 후에 자리를 떠나더라도 질문한 내용에 대한 답을 원격으로 볼 수 있다.
  4. 두 사람이 오목을 같은 버퍼에서 두면 네트워크 플레이를 할 수 있다.

이 외에도 여러가지, 응용할 수 있는 예가 있을 것이다.

단, 2008년 10월초 현재 CVS 버전에서는 GTK 관련 버그가 있어서, GTK는 사용하지 않도록 설치하여 사용하여야 한다.

설치법

젠투 리눅스

필자는 젠투 리눅스를 주로 사용하므로 젠투 리눅스에서 설치하는 방법을 먼저 소개하겠다. 젠투에서는 CVS 버전이 패키지로 제공되므로 매우 간단하게 설치가 가능하다. USE flag에 -gtk를 준 다음 emacs-cvs 패키지를 설치하면 된다.

# echo '>=app-editors/emacs-cvs-23.0.0 ~amd64' >> /etc/portage/package.keywords
# echo 'app-editors/emacs-cvs -gtk' >> /etc/portage/package.use
# emerge emacs-cvs

매우 간단하다.

데비안 및 우분투

데비안 및 우분투에서 기본적으로 제공되지는 않지만 Ryan McGuire 라는 분이 설치 패키지를 작성해 두었다. 링크를 클릭하여 들어간 다음, 동영상을 감상하시고 동영상 아래 쯤에 있는 링크를 클릭하여 .deb 파일을 다운로드 한다. 그리고 그 파일을 더블클릭 하거나 dpkg 프로그램을 이용하여 설치하면 된다. Ryan의 말에 따르면 Gutsy와 Feisty에서 동작하는 것을 확인했다고 한다.

기다리기

이맥스 23이 정식으로 배포되면 이 기능이 들어갈 것이라고 한다. 그러니, 별로 설치하고 싶은 깊은 마음이 안 생긴다면, 그냥 기다렸다가 나중에 업그레이드 하면 된다.

2008년 9월 26일 (금)

구글 사전 이맥스에 붙이기

이맥스 유저라면 저의 허접한 ELisp 프로그램을 써 보세요.

특징과 사용법

다음과 같은 특징들이 있습니다.

  • 아까 설명한 Bash 스크립트와 같은 특징들.
  • 현재 커서가 있는 곳의 단어가 기본으로 들어가 있어서 편리합니다.
  • 제가 작성한 bash 스크립트가 있어야 동작합니다.
  • 색깔 있는 출력. (ansi-color.el 이 있어야 합니다.)
  • 현재 창이 아닌 다른 창에 사전 검색 결과를 보여주고, 커서는 원래 위치에 계속 있기 때문에 귀찮지 않습니다.
  • 사전 검색 결과 내에서 다시 검색할 때에는 현재 버퍼에에 커서가 그대로 있습니다.
  • 이맥스 22와 23에서만 테스트 해 봤습니다.

"C-c s"를 단축키로 지정해서 사전을 볼 수 있습니다. 찾고 싶은 단어 위에서 "C-c s"를 누르면 더 편리합니다. 사전 버퍼로 이동해서 단어를 더 찾아도 됩니다. 현재 커서에 있는 단어가 기본으로 나타나는 것이 불편하면 /를 입력한 뒤에 새로 입력할 수 있습니다. 즉 a/b로 단어가 입력되면 b가 사전에서 찾아집니다. (쉘 스크립트에서 구현되어 있음.)

어디다가 쓸 것인지…

이게 어디에 유용할까요? 다음과 같은 상황에서 저는 이것을 이용합니다.

  • 소스 코드 읽다가 주석에 있는 단어 찾을 때
  • 이맥스에서 이맥스 매뉴얼 읽을 때
  • w3m-emacs로 이맥스에서 웹서핑 할 때
  • 이맥스에서 이메일 쓸 때
  • org-mode로 이맥스에서 노트 작성할 때

C-c s를 눌러서 단어의 뜻을 찾아봅니다.

설치 및 설정

수동 설치

google-dictionary.el 내용을 복사해서 붙인 다음에 평소에 ELisp 코드를 저장하던 곳에 붙여 넣어서 저장하세요. 그 다음에 이맥스 설정 파일에 다음 네 줄을 집어 넣으세요. 보통 ~/.emacs 파일입니다. 무조건 붙여 넣지 말고 디렉토리를 맞게 바꿔서 넣으셔야 합니다. google-dictionary.el 이 있는 디렉토리와 dict 프로그램이 있는 디렉토리 말이죠.

;; Dictionary
(load-file "path/to/google-dictionary.el")
(setq dictionary-program "path/to/dict")
(global-set-key "\C-cs" 'dictionary-search)

아래에 있는 것이 google-dictionary.el 파일 내용입니다. 이것을 복사 붙여넣기 하여 google-dictionary.el 파일로 저장하세요.

;; google-dictionary.el -- an interface to dict

;; Author: Jaehyun Yeom
;; Keywords: interface, dictionary
;; Depends on: dict--a bash script to fetch google dictionary

;; Invokes dict script and shows on the other window

(require 'ansi-color)

(defun dictionary-search (word &optional lang-from lang-to)
  "Search the word from Google dictionary. It just invokes dict
 script. With c-u you can select lang-from and lang-to.
  "
  (interactive
   (list (read-string "Search word: " (current-word))
         (if current-prefix-arg
             (read-string "Lang from: " "ko")
           "ko")
         (if current-prefix-arg
             (read-string "Lang to: " "en")
           "en")))
  ;; if called by pressing the button
  (unless word
    (setq word (read-string "Search word: ")))
  ;; Just in case non-interactively called
  (unless lang-from
    (setq lang-from "ko"))
  (unless lang-to
    (setq lang-to "en"))
  (setq old-buffer-name (buffer-name))
  (if (get-buffer "*dictionary*")
      (kill-buffer "*dictionary*"))
  (unless (boundp 'dictionary-program)
    (setq dictionary-program "dict"))
  (call-process "bash" nil "*dictionary*" nil dictionary-program "--color" word lang-from lang-to)
  (if (equal old-buffer-name "*dictionary*")
      (switch-to-buffer "*dictionary*")
    (switch-to-buffer-other-window "*dictionary*"))
  (ansi-color-apply-on-region 0 10000)
  (beginning-of-buffer)
  (unless (equal old-buffer-name "*dictionary*")
    (other-window -1)))

제가 작성한 dict 쉘 스크립트가 있어야 동작합니다. 제가 작성한 다른 포스팅을 보시면 구글 사전 bash 쉘 스크립트가 있습니다. 이것이 있어야 합니다.

구글 사전 bash 쉘 스크립트

이번에 구글 사전이 새로 모습을 드러내었는데, 속도가 빨라서 매우 좋습니다. 그래서 구글 사전 컨텐츠를 긁어오는 허접한 스크립트를 작성해 보았습니다. 저는 유용하게 쓰고 있습니다. 예전에 올렸던 사전 패키지와는 다른 것이고, 제가 영문으로 포스팅한 것을 한글로 쓴 것입니다.

특징과 사용법

다음과 같은 특징이 있습니다.

  • 구글 사전에서 자료를 긁어온다.
  • 쉘에서 dict foo 라고 입력하면 foo를 구글 사전에서 찾는다.
  • 알록달록한 ANSI 출력이 가능하다. dict --color foo 라고 입력하면 컬러풀한 출력이 나온다.
  • bash, wget, sed, awk 등이 설치되어 있어야 한다.
  • 인터넷 접속이 되어야 한다.(google.com)

그냥 dict 만 입력하면 사용법을 볼 수 있다.

설치

아채 스크립트를 복사해서 붙여넣은 다음에 ~/bin/ 등의 디렉토리에 넣는다. 없으면 mkdir ~/bin 으로 만들면 된다.

#!/bin/bash
# This is a -*- sh -*- file
#
# Author: Jaehyun Yeom
#
# Show definition of the word from Google dictionary.

if [[ "$#" -gt 0 && "$1" =~ '--colou?r' ]]; then
  export DICT_COLOR=on
  shift
fi

# Basic Arguments
SCRIPT_NAME=$(basename "$0")
# Naively url encode WORD, such as %20
WORD=$(basename "$1")
WORD=$(echo -n $1 | od -t x1 -A n --width=1000 | tr ' ' '%')
LANG_FROM=$2
LANG_TO=$3

# Default LANG_PAIR is ko|en
: ${LANG_FROM:='ko'}
: ${LANG_TO:='en'}
LANG_PAIR=`echo -n "$LANG_FROM|$LANG_TO" | od -t x1 -A n --width=1000 | tr ' ' '%'`

# Get URL
URL="http://www.google.com/dictionary?langpair=$LANG_PAIR&q=$WORD&hl=ko&aq=f"

if [ $# -eq 0 ] || [ "$1" == '-h' ] || [ "$1" == '--help' ] || [ "$1" == '-?' ]; then
  cat <<EOF
Usage:    $SCRIPT_NAME [--color] word [lang_from=ko] [lang_to=en]
Show definition of the word from Google dictionary.

Parameters:
  --color        colorful output

Example:
    $SCRIPT_NAME --color sun
    $SCRIPT_NAME 태양
    $SCRIPT_NAME sonne de
    $SCRIPT_NAME sun en de

Tips:
    If you like colorful output, you can alias it like the following.
        alias $SCRIPT_NAME=$SCRIPT_NAME --color

    If you are not using utf-8, pipe this script to iconv with -c option.
        $SCRIPT_NAME hello | iconv -c -f utf-8 -t euc-kr

Bugs:
    Some bugs for parameter parsing.

EOF
else
  echo $URL
  wget $URL -q -O - \
    | awk 'BEGIN { ORS = ""; if (ENVIRON["DICT_COLOR"]) color = 1 }

           /<ul id="definition">|<div class="error">|<h2[^>]*>/ { visible = 1 } 
           visible==0 { next }

           /<br \/>|<\/li>|<\/h2>|<\/div>/ { print "\n" }
           /<\/h2>|<\/samp>|<\/h4>/ && color { print "\x1b[0m" }
           { print $0 }
           /<samp>/ { print color ? "\x1b[m\x1b[01;34m      + " : "      + " }
           /<span class="translate">/ { print "    " }
           /<ol>/ { num = 1; print "\n" }
           /<\/ol>/ { num = 0 }
           /<li>/ && num > 0 { print "    " num++ ". " }
           /<h2[^>]*>/ && color { print "\x1b[m\x1b[01;37m" }
           /<h4[^>]*>/ && color { print "\x1b[m\x1b[01;32m" }
           /<div class="error">/ && color { print "\x1b[m\x1b[01;31m" }

           /<\/ul>|<\/div>|<\/h2>/ { visible = 0 }
           '\
    | sed 's/[[file:ltxpng/google-dictionary-external_0001.png][file:ltxpng/google-dictionary-external_0001.png]]\?<[^>]*>//g'
fi

실행할 수 있도록 chmod +x 를 해 준다.

chmod +x ~/bin/dict

설정

패스 설정

~/bin 이 패스로 걸려있지 않다면, .bashrc 파일에 다음 내용을 추가한다.

export PATH=~/bin:"${PATH}"

~/bin 에 있는 실행 파일들이 먼저 수행되므로 해당 패스에 실행 파일을 넣으면 오버라이드 할 수 있다.

기본으로 색깔있게 출력하기

xterm 같은 ANSI 지원 터미널을 이용한다면, 다음 alias를 .bashrc 혹은 평소에 alias를 넣어두는 곳에 넣는다.

alias dict='dict --color'

혹은

alias dict='dict --colour'

csh 를 쓴다면, 다음과 같이 써 넣자.

alias dict dict --color

현재 쉘에 적용시키기

다시 쉘을 시작하지 않고 다음 명령을 내려 현재 쉘에 바뀐 설정을 적용시킬 수 있다.

source ~/.bashrc

Google Dictionary on Emacs

If you're an emacs user you can use my naive ELisp program.

Features and Usage

Has following features.

  • Same as above bash script.
  • Default string becomes current word that you're pointing for convenience.
  • Depends on my bash script.
  • Colorful font-lock output. (depends on ansi-color.el)
  • Shows dictionary contents at other window and doesn't move focus there.
  • Searching inside the dictionary buffer just replaces contents for convenience.
  • I've only tested thie ELisp on Emacs-22 and 23.

You can "C-c s" to look through dictionary. It's more convenient if you "C-c s" on the word that you're like to look through. You can also move to dictionary buffer and search more. If you don't like default string from current word, you can simply type / and the word you'd like to search. That is, if you type a/b, b will be searched. (bash script implements this.)

What makes it useful?

Then, what makes it useful? Well, I use Google Dictionary on Emacs when I am…

  • reading source code and ike to find words in its comments,
  • reading Emacs info,
  • surfing the net using w3m-emacs,
  • writing email from Emacs,
  • writing notes with org-mode,

I just C-c s to see the definition of words.

Installation and Configuration

Manual Install

Copy and paste google-dictionary.el and save it where you store elisp codes. And add the following lines to your dot emacs file.

;; Dictionary
(load-file "path/to/google-dictionary.el")
(setq dictionary-program "path/to/dict")
(global-set-key "\C-cs" 'dictionary-search)

The following is google-dictionary.el file.

;; google-dictionary.el -- an interface to dict

;; Author: Jaehyun Yeom
;; Keywords: interface, dictionary
;; Depends on: dict--a bash script to fetch google dictionary

;; Invokes dict script and shows on the other window

(require 'ansi-color)

(defun dictionary-search (word &optional lang-from lang-to)
  "Search the word from Google dictionary. It just invokes dict
 script. With c-u you can select lang-from and lang-to.
  "
  (interactive
   (list (read-string "Search word: " (current-word))
         (if current-prefix-arg
             (read-string "Lang from: " "ko")
           "ko")
         (if current-prefix-arg
             (read-string "Lang to: " "en")
           "en")))
  ;; if called by pressing the button
  (unless word
    (setq word (read-string "Search word: ")))
  ;; Just in case non-interactively called
  (unless lang-from
    (setq lang-from "ko"))
  (unless lang-to
    (setq lang-to "en"))
  (setq old-buffer-name (buffer-name))
  (if (get-buffer "*dictionary*")
      (kill-buffer "*dictionary*"))
  (unless (boundp 'dictionary-program)
    (setq dictionary-program "dict"))
  (call-process "bash" nil "*dictionary*" nil dictionary-program "--color" word lang-from lang-to)
  (if (equal old-buffer-name "*dictionary*")
      (switch-to-buffer "*dictionary*")
    (switch-to-buffer-other-window "*dictionary*"))
  (ansi-color-apply-on-region 0 10000)
  (beginning-of-buffer)
  (unless (equal old-buffer-name "*dictionary*")
    (other-window -1)))

Don't forget you should have my dict bash program to use it.

Google Dictionary Bash Script

I wrote a naive bash script that fetches and shows Google dictionary contents.

Features and usage

Has following features.

  • Fetching data from Google Dictionary.
  • Just type dict foo to find foo from Google Dictionary.
  • Colorful ANSI output. Type dict --color foo for colorful output.
  • Depends on bash, wget, sed, awk.
  • Internet connection required. (to google.com)

You can just type dict to see usage.

Installation

Please copy and paste this humble bash script and save it somewhere like ~/bin/. If you don't have the directory do mkdir ~/bin to create the directory.

#!/bin/bash
# This is a -*- sh -*- file
#
# Author: Jaehyun Yeom
#
# Show definition of the word from Google dictionary.

if [[ "$#" -gt 0 && "$1" =~ '--colou?r' ]]; then
  export DICT_COLOR=on
  shift
fi

# Basic Arguments
SCRIPT_NAME=$(basename "$0")
# Naively url encode WORD, such as %20
WORD=$(basename "$1")
WORD=$(echo -n $1 | od -t x1 -A n --width=1000 | tr ' ' '%')
LANG_FROM=$2
LANG_TO=$3

# Default LANG_PAIR is ko|en
: ${LANG_FROM:='ko'}
: ${LANG_TO:='en'}
LANG_PAIR=`echo -n "$LANG_FROM|$LANG_TO" | od -t x1 -A n --width=1000 | tr ' ' '%'`

# Get URL
URL="http://www.google.com/dictionary?langpair=$LANG_PAIR&q=$WORD&hl=ko&aq=f"

if [ $# -eq 0 ] || [ "$1" == '-h' ] || [ "$1" == '--help' ] || [ "$1" == '-?' ]; then
  cat <<EOF
Usage:    $SCRIPT_NAME [--color] word [lang_from=ko] [lang_to=en]
Show definition of the word from Google dictionary.

Parameters:
  --color        colorful output

Example:
    $SCRIPT_NAME --color sun
    $SCRIPT_NAME 태양
    $SCRIPT_NAME sonne de
    $SCRIPT_NAME sun en de

Tips:
    If you like colorful output, you can alias it like the following.
        alias $SCRIPT_NAME=$SCRIPT_NAME --color

    If you are not using utf-8, pipe this script to iconv with -c option.
        $SCRIPT_NAME hello | iconv -c -f utf-8 -t euc-kr

Bugs:
    Some bugs for parameter parsing.

EOF
else
  echo $URL
  wget $URL -q -O - \
    | awk 'BEGIN { ORS = ""; if (ENVIRON["DICT_COLOR"]) color = 1 }

           /<ul id="definition">|<div class="error">|<h2[^>]*>/ { visible = 1 } 
           visible==0 { next }

           /<br \/>|<\/li>|<\/h2>|<\/div>/ { print "\n" }
           /<\/h2>|<\/samp>|<\/h4>/ && color { print "\x1b[0m" }
           { print $0 }
           /<samp>/ { print color ? "\x1b[m\x1b[01;34m      + " : "      + " }
           /<span class="translate">/ { print "    " }
           /<ol>/ { num = 1; print "\n" }
           /<\/ol>/ { num = 0 }
           /<li>/ && num > 0 { print "    " num++ ". " }
           /<h2[^>]*>/ && color { print "\x1b[m\x1b[01;37m" }
           /<h4[^>]*>/ && color { print "\x1b[m\x1b[01;32m" }
           /<div class="error">/ && color { print "\x1b[m\x1b[01;31m" }

           /<\/ul>|<\/div>|<\/h2>/ { visible = 0 }
           '\
    | sed 's/[[file:ltxpng/google-dictionary-external_0001.png][file:ltxpng/google-dictionary-external_0001.png]]\?<[^>]*>//g'
fi

You should set it executable.

chmod +x ~/bin/dict

Configuration

Setting Path

If you don't have execution path to ~/bin, you can add it to .bashrc file the following.

export PATH=~/bin:"${PATH}"

Now you can put your own scripts there and even override scripts because ~/bin is processed first.

Default Colorful Output

It supports colorful output if you're using xterm or other ANSI compatible terminals. So add the following line to .bashrc or .bash_aliases wherever you put your aliases in.

alias dict='dict --color'

or

alias dict='dict --color'

If you're using csh, write like the following.

alias dict dict --color

Applying to existing shell

After that you can apply them to existing shells by the following command.

source ~/.bashrc

2008년 5월 14일 (수)

이맥스 사전 패키지 dictionary

!! 저는 이 사전 패키지보다 구글 사전과 이맥스를 붙인 것을 더 자주 사용합니다. 제 블로그에서 찾아주세요. !!

소개

이맥스에서 메일을 읽거나 웹을 돌아다니다 보면, 사전을 찾아야 할 때가 종종 있다. 그럴 때마다 종이 사전을 뒤적거리거나, 전자 사전을 뒤적거리거나, 혹은 다른 사전 응용 프로그램이나 웹 사전을 이용해 본 경험이 있을 것이다. 이맥스를 사용하는 도중에 가급적이면 이맥스 밖으로 눈을 돌리는 행동을 삼가야 한다. 따라서 이맥스에서 나가지 않고, 사전을 뒤적거릴 수 있는 사전 패키지인 dictionary를 소개하고자 한다.

설치

dictionary 패키지는 RFC 2229에 따라 동작하는 사전 서버와 통신이 필요하므로 사전 서버와의 네트워크 연결이 필요하다. 기본으로 지정된 사전 서버도 잘 동작하므로, 인터넷에 연결되어 있는 PC면 충분하다.

필자는 젠투 리눅스를 이용하므로 다음과 같이 설치를 수행하였다.

# emerge app-emacs/dictionary

데비안을 이용한다면 다음과 같이 패키지 설치를 할 수 있다.

# apt-get install dictionary-el

직접 설치하려면 홈페이지에서 다운로드 한 뒤 적당한 디렉토리에 넣고, 필요하다면 컴파일을 하면 된다. 다른 패키지들의 설치 방법과 동일하다.

사용

다음과 같은 키바인딩을 이용하면 편리하다. 젠투의 경우에는 패키지를 설치하면 기본적으로 다음과 같은 키바인딩이 된다.

;; key bindings
(global-set-key "\C-cs" 'dictionary-search)
(global-set-key "\C-cm" 'dictionary-match-words)

이제 C-c s를 누르고 찾고자 하는 단어를 찾으면 사전 검색 결과가 화면에 나타난다.

커스터마이징

M-x customize-group RET dictionary RET을 하면 커스터마이징 페이지를 볼 수 있다. 여기서 사전 서버를 변경한다거나 다른 세팅들을 할 수 있다.

2008년 3월 6일 (목)

마르코프 연쇄

마르코프 연쇄에 대해서 알아볼 일이 생겨서 위키백과를 참조하는데, 좀 어렵게 설명이 되어 있더군요. 나중에 시간이 생기면 정리를 좀 해 보고 싶습니다. 일단 간략하게 (관리 안 되고 방치되어 있는) 블로그에 알기 쉽게 써 보려고 합니다. 비전공자도 이해할 수 있는 언어로 말이죠.

고전 영화 터미네이터를 보면 미래에 일어나는 일를 막으려고 누군가가 미래에서 찾아오죠. 근데 미래에서 막으면 되지 왜 과거로 오는 것일까요? 그건 미래에 일어나는 일이 과거와 관련이 있기 때문입니다. 과거에 뭔가가 일어났기 때문에 미래에 어떤 일이 일어나는 거죠.

그러면 과거의 어떤 일 때문에 그 일이 일어나는 걸까요? 내가 오늘 지각한 이유는 아침에 늦게 일어났기 때문이지만, 그건 그 전날에 술을 먹었기 때문이고, 술을 먹은 이유는 3년 전에, 소주 회사에서 술의 도수를 낮췄기 때문이다. 이렇게 핑계를 대 볼 수 있습니다. 즉, 오늘 어떤 일이 일어난 이유는 과거에 있었던 무한히 많은 일들이 모두 연관되어 있다는 겁니다.

그런데 너무 복잡합니다. 맞습니다. 너무 복잡합니다. 그런데 이공계의 많은 지식들은 이런 복잡한 문제를 단순하게 보기 시작하면서 문제를 풀어 나갑니다. "세상에 난 수학/과학을 볼 때마다 너무 복잡해서 이해할 수가 없던데, 이걸 간단하다고 하다니..." 라고 생각하시는 분들이 계시겠지만 천만에... 그런 학문들이 없었으면 같은 현상을 이해하기는 훨씬 더 어려울 겁니다. 핫하, 과학은 세상을 더 쉽고 편리하게 만들어 준다니깐요.

잠깐 삼천포로 빠졌습니다. 그러면 어떻게 간단히 해 볼까요? 과거의 무한히 많은 일들 때문에 오늘 이런 일이 일어났다고 생각하지 말고, 과거의 유한(!)한 몇몇 일 때문에 오늘의 일이 일어났다고 생각해 보는 겁니다. 이것이 마르코프의 가정(가설)입니다. 이 가정에 따르는 일련의 과정을 마르코프 과정 혹은 마르코프 연쇄라고 합니다.

그러면 내친김에 아주 간단하게 해 봅시다. 오늘 어떤 일이 일어나는 이유는 바로 전날에 일어난 일 때문이다. 어떤 사건은 그 일이 일어나기 바로 전에 있었던 일과 관련이 있고, 그 둘 사이의 확률로 나타내어 보는 것입니다. 즉, "어제 그녀가 나에게 미소를 지어 주었으니 오늘 그녀가 마찬가지로 나에게 미소를 지어줄 확률이 70 퍼센트는 되겠군." 혹은 "어제 그녀가 화가 났는데 오늘 화가 안 풀렸을 확률이 70 퍼센트 정도는 되겠군." 이런 것이 됩니다. (후... 저랑 별로 관련없는 것으로 예를 들려니 힘들군요. 제발 관련 좀 있게 해 주세요.)

다시 말하자면 방금 설명한 제 마음 속의 여인은 그날의 기분은 바로 전날의 기분에 따라서만(!) 좌우됩니다. 바꾸어 말하면 오늘의 기분에서 일정한 확률로 계산되어 내일의 기분이 결정되는 겁니다. (헉-_- 로보트인가.) 이것이 1차 마르코프 연쇄입니다.

이틀 전의 일까지 오늘의 기분에 직접적으로 영향을 끼치는 조금 더 복잡한 그녀는 2차 마르코프 연쇄를 따르시는 분이 됩니다. 즉, "그녀의 어제의 기분은 어땠고, 이틀전의 기분은 어땠기 때문에 오늘의 기분이 이럴 확률은 몇 퍼센트이군." 이렇게 그녀의 기분을 계산(-_-) 할 수 있다는 겁니다. 대신에, 2년 전에, 어느 날에 기분이 나빴기 때문에 오늘 기분이 나쁘다고 하지는 않습니다. 정확히 말해서, 직접적으로 영향을 받지는 않습니다.

1차 마르코프 연쇄를 따르는 그녀는 바로 전날의 기분에 따라서 오늘의 기분이 좌우되는 이상적인(-_-) 분이시지만, 바로 전날의 기분은 그 전날의 기분에 따라 좌우되기 때문에, 결국 연쇄적으로 따진다면 오늘 그녀의 기분은 태어날 때의 기분과도 관련이 있다는 겁니다. 사슬처럼 연결이 되어 있죠. 그치만, 오늘 그녀의 기분을 예상하는데에는 바로 전날의 기분만 알고 있으면 땡이죠! 수학은 이래서 삶에 도움이 되는 겁니다. 헛헛허... (과연...)

좀 다른 문제를 생각해 보죠. 가위 바위 보를 해서 계단을 올라가는 놀이는 예전에 해 보셨겠죠. 좀 더 높은 확률로 이기려면 바로 전에 상대가 무엇을 냈고 그 다음에 무엇을 내는지를 통계를 낸다면, 골고루 분포되어 있지는 않을 겁니다. 이걸 이용하면 좀 더 많이 이길 수 있을 겁니다. 음악 작곡을 하는데 어떤 음이 나온 뒤에 어떤 음이 나올지에 대한 확률을 구하여 기계 작곡에 도움을 얻을 수도 있을 겁니다. 물론 바로 전에 것 뿐만이 아니라, 그 전에 있는 것까지 볼 수도 있겠지요. (앞에 몇 개까지냐에 따라서 1차, 2차, 3차 ... 가 됩니다.)

자연과학적 개념들을 일단 이게 뭔지 이해를 하고 나면 똑같은 책을 보는데도 훨씬 쉬워집니다. 즉, 같은 마법사의 책(알 수 없는 고대의 언어로 씌여진 이공계 서적)을 본다고 해도 대충 개념이 잡혀 있는 상태에서 보는 경우와 그렇지 않은 상태에서 보는 경우에 개념을 이해하는데 소모되는 칼로리의 양과 쌓이는 스트레스의 강도는 급격히 달라지게 됩니다. 저는 책들이 개념 설명을 좀 쉽게 해 줬으면 좋겠습니다. 각 장의 첫 번째 절에서 쉽게 개념을 잡아주고, 뭔놈의 법칙, 뭔놈의 법칙을 알려줬으면 하는데, 처음부터 외계어로 설명하면 상당히 괴롭습니다. 물론 좋은 책들은 앞 부분에서 개념을 잡아주어 안드로메다로 가는 것을 막아줍니다.

2007년 12월 9일 (일)

보안은 오페라가 최고

가끔 웹 브라우저의 보안 결함을 비교하고 있는데 시큐니아( http://secunia.com ) 라는 사이트에 가면 각 소프트웨어의 알려진 보안 결함이 나타납니다.

각 브라우저의 최신버전의 보안 결함의 수를 매번 비교해 보고 있는데, 검색할 때마다 오페라는 0입니다.

지금 글을 쓰는 현재, IE7은 7개, 파이어폭스는 6개, 사파리는 3개, 컹커러는 2개의 보안 결함이 있네요. 오페라는 0입니다.

이것은 결함이 생겼어도 이미 고쳐졌다는 겁니다. 다른 브라우저들은 최신 버전으로 업데이트해도 여전히 알려진 결함이 있다는 의미이구요. 오페라는 보안 결함이 발생해도 빠른 시간 내로 수정 업데이트가 되더군요. 잘 발생하지도 않구요. 오페라 보안 취약점이 발견되었다는 기사들을 보면 해결 방법은 최신 버전으로 업데이트하는 것이라고 나오는 경우가 대부분입니다. 이것은 최신 버전에서는 결함이 수정되었다는 의미로써, 최신 버전으로 유지가 필요합니다.

2007년 7월 15일 (일)

이맥스에서 지메일의 SMTP 이용하기

나는 오페라 웹 브라우저의 메일 클라이언트인 M2이맥스를 메일 클라이언트로 이용한다. 이맥스를 메일 클라이언트로 활용할 때 지메일의 SMTP를 이용하여 메일을 보낼 수 있는데 이 방법을 소개하고자 한다.

지메일의 받고 보내는 서버는 다음과 같다.

서버 종류설정
받는 메일 서버 (POP3)pop.gmail.com
SSL: 사용함
포트: 995
보내는 메일 서버 (SMTP)smtp.gmail.com (인증 사용)
STARTTLS: 사용함
포트: 465와 587

이것은 지메일에서 POP 신청을 한 후에 이용 가능하다. 만약 이미 지메일을 다른 메일 클라이언트에서 이용하고 있다면 이미 신청을 한 것이다. 그렇지 않다면 지메일 설정에 가서 POP 신청을 하면 된다. 신청을 하면 POP3 뿐만 아니라 SMTP까지 이용할 수 있다.

그렇다면 지메일의 SMTP를 이용하는 방법을 알아보자. 시작 스크립트에 등록하는 방법을 이용할 수도 있고, Customize를 이용하는 방법이 있는데, Customize를 이용하는 방법을 알아보자.

  1. M-x customize-group RET mail RET을 한다.
  2. 사용자 이름 등 필요한 설정을 하고 C-x C-s를 하여 설정을 저장한다.
  3. M-x customize-variable RET send-mail-function RET을 한 뒤 smtpmail-send-it으로 설정한다. 이것을 하지 않으면 sendmail 프로그램을 통하여 메일이 전송된다. C-x C-s로 저장한다.
  4. M-x custoimze-variable RET message-send-mail-function RET을 한 뒤 message-smtpmail-send-it을 설정한다. C-x C-s로 저장한다.
  5. ~/.signature 파일에 자신의 메일 서명을 쓰고 저장한다.
  6. M-x customize-group RET smtpmail RET를 한다.
  7. smtpmail-default-smtp-server와 smtpmail-smtp-server를 smtp.gmail.com 으로 설정한다.
  8. smtpmail-smtp-service를 587로 설정한다.
  9. smtpmail-starttls-credentials에서 State를 누르고 :키를 눌러 LISP 형태로 나타나게 한다. 이렇게 하는 이유는 nil을 입력할 수 있는 방법이 없게 되어 있는 문제점이 있기 때문이다.
  10. 빈 칸에 '(("smtp.gmail.com" 587 nil nil))를 입력한다.
  11. smtpmail-auth-credentials에 Value Menu를 누르고 Repeat를 선택한다.
  12. INS 버튼을 누른 다음에 필요한 내용을 집어 넣는다. 여기서 사용자 이름에는 반드시 @gmail.com까지 포함시켜 완전한 이메일 주소를 넣어야 한다.
  13. 패스워드는 넣지 않으면 보낼 때마다 물어본다.
  14. C-x C-s를 눌러 설정 파일을 저장한다.

만일 패스워드를 매번 입력하는 것이 귀찮지만 이맥스 설정 파일에 패스워드를 집어넣는 것이 마음 편하지 않다면 따로 netrc 형태의 파일을 만들고 거기에 집어넣고 permission을 변경하는 방법이 있다.

임의의 파일을 하나 생성하여 퍼미션을 600으로 준 뒤 다음과 같이 입력하고 저장하고 M-x customize-variable RET smtp-auth-credentials RET를 하고 Value Menu를 눌러 파일 형태를 선택하고 그 파일 이름을 입력하면 된다.

machine smtp.gmail.com login username@gmail.com password PASSWORD

이제 C-x m을 눌러 메일을 작성한 뒤 C-c C-c를 눌러서 메일을 전송하면 된다. 먼저 자기 자신에게 테스트 메일을 보내본다. 언어 설정 등이 제대로 되어 있다면 한글로 보낸 경우에도 깨지지 않고 메일이 잘 전송될 것이다.

VM을 이용하는 사람들은 Bill Clementson's Blog: VM and Gmail Setup for Dummies를 참고한다.