2007년 4월 24일 화요일

lifepod API 인증 C (libsoup) 예제

며칠에 걸쳐서 잠깐 잠깐씩, 재미가 없으면 내버려뒀다가 다시 생각나면 좀 만져보고 하는 일을 반복한 끝에...

lifepod openAPI의 인증 부분을 짰습니다.  며칠에 걸쳐서 lifepod API 서버의 버그로 잠깐 헤매다가, open ID에 http://를 넣었다가 뺐다가, 해시 부분을 소문자로 써야 하는 API의 숨겨진 사실을 깨닫기도 하다가, gnutls_fingerprint() 함수의 버그성 동작에 헷갈리다가 하면서 인증 부분만 완료.

  • 상단의 openid, userkey, appid, appkey는 자기에 맞게 바꾸세요.
  • 컴파일은 gcc -o example-lifepod `pkg-config --cflags libsoup-2.2` example-lifepod.c `pkg-config --libs libsoup-2.2`
  • 스프(libsoup)는 GObject를 이용해서 만든 것이라서, synchronous하게 쓰려면 상관없지만 asynchronous하게 사용하려면 glib main loop가 필요합니다.

사실 인증보다 더 어려운 건 response로 날아온 XML을 파싱하는 거지만 그건 다른 종류의 내공이니까요. evolution frontend를 만드려고 했지만, evolution이 내부 데이터로 ical을 쓰고 있기 때문에 이건 lifepod xml to/from ical 변환 프로그램 만드는 꼴이 될 듯 합니다. 또 잠시 접어뒀다가 생각나면 조금씩 해 봐야죠.

그놈 패널 배경 그림..

"Just works"의 UI 철학에 따라(?) 패널에 배경 이미지를 씌울 수 있는 기능이 왜 있는지 잘 이해가 되지 않았다.  마침 그놈 사용자 안내서를 읽다가 드래그로 그놈 패널의 배경을 설정할 수 있다는 걸 발견, 노틸러스에서 "바탕색 및 꼬리표" 대화 창을 띄웠다.

사용자 삽입 이미지


얼마전 예비군 훈련을 갔다와서 빨아 놓은 군복을 정리하지 않은 지금...  "위장" 색을 패널에 끌어 놓아 보기로 했다.  (예비군 군복보다는 자이툰 부대 군복이랑 비슷한 것 같지만..)

앗!  정말 "위장" 효과가 있다.  패널이 눈에 잘 안 들어온다.

사용자 삽입 이미지

이렇게 하면 오히려 시야가 애플리케이션 창에 집중되서 패널에서 아무리 CPU 로드 그래프를 그려대고 깜박이는 거에 상관없이 집중할 수 있다. 

단, 패널을 볼 때도 패널이 눈에 잘 안 들어온다...

pootle의 가장 큰 문제는

온라인 번역 시스템으로 pootle을 심각하게 고려해 보았으나 결론은...

못 쓰겠다 (적어도 지금은)

그리 나쁘다라는 게 아니다. pootle은 매우 간결하고, PO 파일을 백엔드로 사용해서 그런지 PO 파일 번역에 좋고 훌륭하게 처리한다. 사용자별로 권한레벨을 분리해 놓았고, translation-toolkit에 잘 구성되어 있는 파이썬 모듈을 이용해 메세지별로 온갖 체크를 온라인에서 할 수 있다.  (여기에 KPC를 붙이면 금상첨화)  이정도만 되도 일단 시작하는 데 문제가 될 일이 없다. launchpad가 우분투 스케줄대로 움직이고 closed source이기 때문에 직접 컨트롤할 수 있는 웹 번역 시스템은 pootle이 유일하다. 하지만, 결정적인 문제는,

기록이 안 남는다

아무리 후지고 완성도가 시스템이라고 해도 개선의 여지가 있으면 그래도 일단 돌리고 볼 텐데, 당장 반달리즘으로 작업물이 날아갈 수 있다는 점은 참 어려운 이야기이다. 백엔드가 PO 파일이기 때문에 PO 파일을 그냥 고쳐버린다. RDBMS를 쓸 필요까지야 없겠지만 백엔드의 한계때문에 변경 사항의 기록을 남기도록 고치는 것도 매우 힘들다.

2007년 4월 14일 토요일

Linux x86 64 환경

데스크탑을 core2duo로 업그레이드하면서 64비트 리눅스의 세계로 빠져들었다.

사실 현재 시점에서는 64비트 데스크탑 OS는 별 메리트가 없다.  좀 더 시간이 지나서 대용량 홈 비디오 편집이라던가 그런게 일반화되거나, 엄청난 양의 데이터를 메모리에 갖고 있어야 하는 컴퓨터 게임이 나오거나 해야 상황이 달라지려나?  요즘의 기껏해야 전체 메모리가 수 GB 내외인 데다가 애플리케이션도 GB 단위의 메모리를 사용하는 애플리케이션이 없는 현재 상황에서는 메리트가 없다.

어쨌든...  새로운 세계의 도전을 핑계삼아 최근에 시도한 바로는 몇가지 걸림돌이 있었다.  문제는 과거의 ia32 바이너리들이다.

vmplayer - 의외로 그냥 까니까 동작했다.  호환 라이브러리만 (데비안/우분투의 경우 ia32-libs 및 ia32-libs-gtk) 잘 설치해서 쓰면 문제가 없다.  단 imhangul을 쓰고 있다면 imhangul도 GTK+ 호환성 라이브러리에 맞게 깔아야 해서 xim/nabi로 쓰는 게 속편하다.

enemy territory - 왜 nvidia glx가 호환성 라이브러리가 (nvidia-glx-ia32) 있는지 생각하다가 테스트해 보려고 설치해 본 것.  역시 설치하니까 아주 잘 된다.  (왠지 옛날보다 많이 죽는 느낌이 드는 것 같은데 -_-)

flash - 첫 번째 걸림돌.  standalone program이 아니라 다른 프로그램의 플러그인이기 때문에 골치가 아프다.   Adobe에서는 64비트 포팅은 다시 빌드하거나 포인터 변수 사용 고치는 정도의 간단한 문제가 아니고 현재 작업중이라고 한다.  가장 쉽고 간단한 방법은 nspluginwrapper를 이용하는 것.  일반 플러그인을 사용하는 것보다 꽤 CPU 로드가 높아서 (그래도 쓸만하지만) 궁극적인 솔루션이라고 할 수는 없다.

duncan:~/tmp$ tar zxvf install_flash_player_9_linux.tar.gz
...
duncan:~/tmp$ cd install_flash_player_9_linux
duncan:~/tmp/install_flash_player_9_linux$ linux32 flashplayer-installer
...
duncan:~/tmp/install_flash_player_9_linux$ nswrapper ~/.mozilla/plugins/libflashplayer.so

Java - 이게 가장 큰 문제이다.  sun java 1.4는 nspluginwrapper로 동작하지만, java5/java6는 동작하지 않는다.  IBM JRE도 AMD64 버전이 없긴 마찬가지이다.  blackdown java는 원래 java5/java6 버전이 없다.  이 문제는 Sun의 게으름과 무관심이 가장 큰 문제이다.  이미 JRE는 amd64로 포팅이 되어 있고 없는 부분은 plugin 인터페이스뿐이다.  flash와는 달리 컴파일만 하면 해결되는 문제라는 얘기다.  J2SE 소스코드를 받아서 고쳐보려다가 참고 그냥 blackdown java만 깔아 놓았다.  이래서 자바가 진작에 오픈소스가 됐어야...

브라우저 문제를 해결하는 가장 현실적인 방법은 32비트로 빌드한 웹브라우저를 사용하는 것이다.  swiftfox를 사용하는 게 한 가지 방법이다.


결론적으로...  현재 새로 까는 사람들은 그냥 32비트 리눅스를 설치하기를 권장 -_-  (과연 ia32는 언제까지 존속할 것인가?)   일단 현재까지의 내용만 정리해서 flash 데비안 패키지도 nswrapper 사용하도록 고쳐서 버그 보내 보고 데비안 imhangul 패키지도 ia32 호환 버전 빌드하고 할 예정. 

2007년 4월 9일 월요일

데비안의 선호 투표 해석하는 방법

데비안은 가끔 "투표"를 한다. 데비안 개발자라면 투표권이 주어지는데, 마침 2007년 리더 투표가 있었고 어제 Sam Hocevar가 당선되었다는 결과 발표가 있었다.  음 그런데 이 웹페이지를 보면 대체 무슨 소리인지 잘 알기 어렵다. 뭔가 복잡한 용어가 등장하면서 수식이 등장해서 잘 파악하기 어렵다. 나도 처음에는 상당히 생소했기에, 한번 이 글에서 데비안의 투표 방법인 Condorcet method를 설명해 보려고 한다.

투표자의 갈등

우리가 정치 활동을 하면서 하게 되는 투표는 그 방법이 아주 간단하다. 후보가 몇 개가 있으면 그 중에서 자기가 원하는 후보를 하나 찍고 가장 많은 투표를 획득한 후보가 당선/선택된다. 하지만 찬반투표가 아니라면 실제로 우리가 투표를 할 때도 흔히 고민하게 되는 다음과 같은 문제가 있다.

  • 사표 방지 심리 - 내가 좋아하는 후보는 따로 있는데, 당선이 안 될 것 같다.  차라리 정책이 비슷하면서 당선 가능성이 있는 사람에게 표를 몰아주고 싶다.
  • 절대적 지지 후보 없음 - 이 놈이나 저 놈이나 다 똑같다. 역시 당선 가능성 있는 사람중에 차악을 선택해야 되나?
우리 일상생활의 "한 표"의 행사라는 게 그렇게 고민덩어리이다. 내가 가진 신성한 이 표가 너무 무의미해 보인다. 결국 유행에 밀려 유력 후보를 찍을 수밖에 없고, 정치적인 성향이 분명치 않은 사람은 자기 의견을 투표로 표현하기도 어렵다.

선호 관계 투표

Condrcet method에서는 적어도 위와 같은 고민은 없다.  투표를 할 때 한 사람을 찍는 게 아니라, 여러 명의 선호 관계를 기술하는 방식이기 때문이다.  데비안의 투표 용지를 이용해 보면,

[   ] Choice 1: cwryu
[   ] Choice 2: crew
[   ] Choice 3: clue
[   ] Choice 4: None Of The Above
투표용지는 1234 네 가지의 선택 사이의 선호 관계를 기술하게 된다.  더 선호하는 쪽에 작은 번호를 붙인다.  "2 1 3 4"를 써 넣으면 crew가 cwryu다는 좋다는 이야기이고 cwryu가 clue보다 좋다는 이야기이다.  내가 원하는 후보가 choice 1이지만 당선 가능성이 없다면 그냥 원하는 후보를 1로 쓰고 다음에 차악을 최악보다 우선적으로 선택하면 된다. 

[ 2 ] Choice 1: cwryu
[ 1 ] Choice 2: crew
[ 3 ] Choice 3: clue
[ 4 ] Choice 4: None Of The Above

그래도 좀 나은 후보가 1번인데 2번이나 3번이나 다 똑같은 놈같으면?  Choice 1에 1번을 붙이고 2/3번에 같은 번호를 쓰면 된다.

[ 1 ] Choice 1: cwryu
[ 2 ] Choice 2: crew
[ 2 ] Choice 3: clue
[ 3 ] Choice 4: None Of The Above

개표

왜 이렇게 써 넣어도 내 의견이 반영될 수 있는지, 개표 과정을 설명하면... 

이렇게 수집된 투표는 수 많은 "A to B" 선호의 집합이라고 할 수 있다.  몇 가지 후보 중에서 후보가 A와 B 둘이라고 할 때 A를 B보다 선호하는 사람이 있을 수도 있고 B를 A보다 선호하는 사람이 있을 수 있다.  2007년 리더 투표 페이지의 beat matrix가 그 투표를 모아 놓은 것이다. 그러면 각각의 pair에 대해서 어느쪽을 선호하는 사람이 더 많은 지 생각할 수 있다. 각각의 후보를 node로 하고, 후보 A가 B보다 우세하다는 관계를 A to B의 edge로 표현하면 웹페이지에 나온 그래프가 된다. 

즉 사표 방지심리를 가질 필요없이, 내가 선호하는 후보를 1번으로 기입하면 설령 그 후보가 당선권에서 멀어지더라도, 차악을 선택할 수 있고 그 표가 똑같이 반영된다.  절대적으로 선호하는 후보가 없더라도 특히 싫어하는 후보를 명시한다면 그 후보를 떨어뜨리는 정도로라도 자기 표로 의사를 표현할 수 있다.

애매한 경우

이 그래프가 이번 2007년 리더 투표처럼 한 사람이 모든 사람을 이기면 관계가 없지만 (이런 식으로 결과가 나오는 게 보통), 이 관계가 circular relation이 된다면 복잡해 진다.

이런 상황에서는 (오리지널 Condorcet method는 이 상황에 대한 해결 방법이 없지만) Schulze dropping 방법에 따라 몇 가지 기준에 따라 승자를 결정한다.  첫 번째로 선호 관계에서 얼마나 더 많은 후보를 제쳤느냐가 기준이 된다. 즉 그래프에서 outgoing edge와 incoming edge의 개수를 기준으로 승자를 결정할 수 있다.  그리고 만약에 이걸로도 결정할 수 없는 상황이 된다면, 그렇게 circular relation이 되는 관계만을 추려낸 다음에 가장 약한 (표 차이가 작은) 선호관계를  하나씩 없애서 circular 관계가 아닐 때까지 계속 한다.  예를 들어 A와 B와 C가 circular하게 선호하는 관계라면 A to B, B to C, C to A의 관계중에 가장 약한 관계를 drop한다.

그래도 동률이라면????

...
...

우리나라 국회의원 선거처럼 나이 많은 사람이 당선되는 건가?   -_-

그 상황이 된다면 투표 알고리즘만으로는 해결할 방법이 없다.  그런 상황이 계속해서 벌어진다면, 과연 투표라는 게 인간 사회의 의사결정 방법으로 올바른 방법인지 진지하게 생각해 볼 필요가 있을 듯...

2007년 4월 7일 토요일

어색한 번역 습관

어색한 메세지 번역을 피하는 팁 몇가지에 이어...서 쓰는 건 아니고 관련 있는 몇 가지 메모:

한자를 붙여 신조어 만들기: 비(非)~, ~자(者), ~기(機)


우리가 일상생활이나 컴퓨터에서 쓰는 단어 중에 분명히 비~, ~자, ~기라는 단어가 있고, 많이 쓰는 건 사실이다.  nonpreemptive/비선점, editor/편집기, constructor/생성자, manager/관리자 등등등..  이러한 단어들이 만들어지게 된 유래가 (상당부분 중국과 일본에서 만든 한자 차용) 옳은지 그른지는 재쳐두고라도, 번역할 때 이러한 단어를 임의로 만드는 일은 정말 바람직하지 않다.   non-free - 비자유?, tokenizer - 구문분석자?, monitor - 감시기?  일단 보기에 어색하다.

이미 익숙하게 쓰는 단어가 아니라면 "비~" prefix가 한글로 붙었을 때 그게 부정의 의미인지 파악하기 어렵고, "~기"나 "~자"를 소프트웨어에 사용하는 것도 우리말에 자연스럽지 않다.

살아있는 소프트웨어

흔히 이야기하는 "수동태를 쓰지 말라"는 번역 팁과 연관있는 이야기로, 우리말에선 생명체가 아닌 존재에 동사를 잘 붙이지 않는다. 무생물은 주체적이지 않다.  (철학적인 문제?)

예를 들어 프로그램의 메세지에서 "I"와 "YOU"를 사용하는 말이 나올 때 상당히 고민하지 않으면 자연스럽게 번역하기 힘들다. 흔히 영문 메세지 원문에서는 소프트웨어가 생명을 가지고 사용자와 대화하는 것처럼 I를 주어로, you를 목적어로 사용하는 경우가 있는데 "나는", "당신은"이라고 번역하기 시작하면 번역 결과물이 상당히 곤란해 진다.

마땅히 무슨 말을 써야 한다는 정답은 없고, 아예 I나 you를 번역문에서 언급하지 않고 충분히 뜻이 통하도록 적당히 번역하는 게 가장 좋은 것 같다.

2007년 4월 4일 수요일

쓰레드별 POSIX locale

홈디렉토리를 정리하다가 예전에 쓰레드별로 다른 로케일 쓰는 방법을 찾아보다가 작성했던 예제 코드 발견.  glibc 전용이긴 하지만 지금 서치해 보니 darwin에도 구현되어 있다.  (MS에는 GetThreadLocale, SetThreadLocale 따위, 꼭 필요한 상황은 많지 않다 보니 그다지 쓰이지는 않는 듯...)

#define _GNU_SOURCE

#include <pthread.h>
#include <locale.h>
#include <time.h>
#include <stdlib.h>

struct tm tm = { 0, };

void *
thread_func()
{
  char buf[256];
  int n;
  locale_t l;

  l = newlocale(LC_ALL_MASK, "ko_KR.UTF-8", 0);
 
  uselocale(l);
  sleep(1);
  n = strftime(buf, 256, "%b %A %a", &tm);
  buf[n] = '\0';
  puts(buf);
  sleep(1);
  n = strftime(buf, 256, "%b %A %a", &tm);
  buf[n] = '\0';
  puts(buf);
  sleep(1);
  n = strftime(buf, 256, "%b %A %a", &tm);
  buf[n] = '\0';
  puts(buf);
  sleep(1);
  n = strftime(buf, 256, "%b %A %a", &tm);
  buf[n] = '\0';
  puts(buf);
  uselocale(LC_GLOBAL_LOCALE);
  freelocale(l);
}

int
main(int argc, char *argv[])
{
  pthread_t t;
  char buf[256];
  int n;

  setlocale(LC_ALL, "C");

  pthread_create(&t, 0, thread_func, 0);
  pthread_detach(t);
  sleep(1);
  n = strftime(buf, 256, "%b %A %a", &tm);
  buf[n] = '\0';
  puts(buf);
  sleep(1);
  n = strftime(buf, 256, "%b %A %a", &tm);
  buf[n] = '\0';
  puts(buf);
  sleep(1);
  n = strftime(buf, 256, "%b %A %a", &tm);
  buf[n] = '\0';
  puts(buf);
  sleep(1);
  n = strftime(buf, 256, "%b %A %a", &tm);
  buf[n] = '\0';
  puts(buf);
 
}

실행해 보면 이렇게 두 개 스레드가 다른 로케일을 사용한다.

$ ./a.out
Jan Sunday Sun
 1월 일요일 일
Jan Sunday Sun
 1월 일요일 일
Jan Sunday Sun
 1월 일요일 일
Jan Sunday Sun