Create TOC

2010년 12월 9일

Vim/빈 줄 삭제하기

아래 명령을 이용해서 빈 줄을 삭제할 수 있다.

:g/^$/d

2010년 10월 20일

core 수에 따른 make의 job 최적화

make 에는 -j 옵션이 있고, 이 옵션은 한번에 수행할 수 있는 job 을 지정하는 옵션이다.

이 job의 수는 총 core 개수 + core 개수의 20% 를 추가하는 것이 가장 효율이 좋다고 알려져 있다. 즉

job 수 = core 개수 + round(core 개수 * 0.2)

가 된다.

즉 Q6600 같이 core가 4개라면 4 + round(4 * 0.2) = 5가 된다.

시스템 마다 이것을 계산하는 shell script를 짜면 아래와 같다.

#~/bin/sh

cores=`cat /proc/cpuinfo | grep cores | wc -l`
jobs=`echo "$cores + $cores*0.2"|bc`
echo $jobs|awk '{print int($1+0.5)}'

2010년 10월 19일

Debian/64bit 환경에서 wine 빌드하기

아래 명령을 통해서 빌드를 위한 패키지를 설치한다.

$ sudo apt-get build-dep wine-unstable

이 상태에서는 실제로 빌드가 되지 않는다. 아래 명령으로 추가 패키지를 설치해야 한다.

$ sudo apt-get install ia32-libs-dev

설치가 끝나면 wine 소스를 받는다(wine 홈페이지에서 소스를 받아도 된다).

$ sudo apt-get source wine-unstable

받은 소스를 풀고 원하는 패치를 한 후에 빌드하면 된다.

$ ./configure
$ make

2010년 9월 24일

Debian/Local Repository 만들기

이 문서는 Debian Linux에서 Local Deb Repository를 만드는 방법을 기술한다.

Debian Linux를 사용하다보면 인터넷의 Repository를 사용하지 않고

  • deb 파일을 다운 받아서 사용
  • google earth 처럼 deb 파일을 생성해서 사용
  • alien을 이용해서 rpm을 변환해서 사용
하는 경우가 있다. 이때 만들어진(다운받은) deb 파일은 Local Repository를 만들어서 관리하면 편리하다.

Local Repository를 만드는 순서는 아래와 같다.

저장소 폴더 생성

$ sudo mkdir -p /var/local/deb

deb 파일 복사

$ sudo cp filename.deb /var/local/deb

Packges.gz 파일 생성

$ cd /var/local
$ su
# dpkg-scanpackages deb |  gzip -9c > deb/Packages.gz

source list 파일 생성

아래 명령으로 source list 파일을 생성한다.

$ sudo vi /etc/apt/sources.list.d/local.list

파일 내용은 아래와 같다

# local package
deb file:/var/local deb/

package list 갱신

$ sudo apt-get update

Matroska(.mkv)의 트랙을 추출/합치기

이 문서는 Debian Linux에서 Mastroska(.mkv) 파일의 트랙을 추출하고 합치는 방법에 대해서 기술한다. 간혹 재생이 잘 안되는 Mastroska 파일은 트랙을 추출/합치는 것 만으로 재생이 잘될 때가 있다.

패키지 설치

아래 명령으로 필요한 패키지를 설치한다.

$ sudo apt-get install mkvtoolnix

트랙 추출

정보 얻기

mkvinfo를 이용해서 추출할 트랙 정보를 얻는다.

$ mkvinfo -s filename.mkv | head -3

아래와 같은 정보를 얻을 수 있다.

Track 1: video, codec ID: V_MPEG4/ISO/AVC, default duration: 33.367ms (29.970 fps for a video track), language: und, pixel width: 640, pixel height: 480, display width: 640, display height: 480
Track 2: audio, codec ID: A_MPEG/L3, default duration: 24.000ms (41.667 fps for a video track), language: und, sampling freq: 48000, channels: 2
I frame, track 1, timecode 0 (00:00:00.000000000), size 36297, adler 0x5bfac6c8

여기서 추출할 track을 결정한다(위 예제에서는 video 1개 audio 1개가 있지만, audo track 이 여러 개인 경우가 있다).

추출

mkvextract를 통해서 트랙을 추출한다.

$ mkvextract tracks filename.mkv 1:video1 2:audio1

트랙번호:파일이름 형식을 사용하면 된다. 만일 1번과 3번 트랙을 추출하려면

$ mkvextract tracks filename.mkv 1:video1 3:audio3

트랙 합치기

mkvmerge를 통해서 추출한 트랙을 합친다.

$ mkvmerge --default-duration 0:29.970fps vidio1 audio1 -o output_filename.mkv

이 때 fps 값은 추출한 video track의 fps를 사용한다.

2010년 9월 2일

Apple script로 특정 명령 실행하기

VirtualBox에서는 VBoxManage 라는 command 도구를 제공하는데, 특정 가상머신을 바로 실행 시킬 수 있다.

Apple script를 이용해서 내가 원하는 가상 머신을 바로 실행 시키는 코드를 작성해보았다.

tell me
	activate
	do shell script "VBoxManage startvm \"Debian Linux\""
end tell

2010년 6월 18일

Safari/유용한플러그인

확장 프로그램

Safari 5.0 부터 확장 프로그램(Extention)이 기능으로 추가되었다. 개발자용 메뉴에서 확장 프로그램 활성화 기능을 켜주면 사용할 수 있다.

기능을 활성화 시킨 후 환경설정을 보면 확장 프로그램탭이 추가되어 있는 것을 확인할 수 있다.

이름설명
AdBlockAdBlock의 Extension 버전.
Gentle Status BarChrome과 비슷한 상태표시줄을 만들어준다.
ClickToFlashClickToFlash의 Extention 버전.
Better Google Reader구글 리더에 몇 가지 기능을 추가해준다.
greader-bgtabs구글 리더에서 v키로 현재 글을 새 tab으로 열 때 배경 탭으로 열리게 해준다.
SafariRestoreSafari를 종료할 때 열려있던 탭 정보를 기억했다가 복구해준다. 비슷한 기능이 SafariStand에도 들어있지만 Extention 쪽이 좀 더 자연스럽게 동작하는 것 같다.
MiddleButtonScroll마우스 가운데 버튼을 클릭하고 마우스를 이동하면 그대로 스크롤이 된다.

SIMBL

SIMBL의 플러그인 형태로 기능을 확장할 수 있다

이름설명
GreaseKitSafari에서 User Script를 사용할 수 있게 해준다.
Safari StandSafari에서 Quick Search등의 기능을 추가해준다
SafariGesturesSafari에서 마우스 제스쳐를 지원해준다.
megazoomerCocoa Application을 전체 화면으로 실행시켜 준다

2010년 6월 6일

OS/X - PyQt 설치

이 문서는 Snow Leopard에서 PyQt를 설치하는 방법을 기술한다.

준비

아래 경로에서 Cocoa 버전의 QT 를 다운 받는다.

http://qt.nokia.com/downloads/qt-for-open-source-cpp-development-on-mac-os-x

아래 경로에서 PyQt를 다운 받는다.

http://www.riverbankcomputing.co.uk/software/pyqt/download

아래 경로에서 sip를 다운 받는다.

http://www.riverbankcomputing.co.uk/software/sip/download

PyQt 빌드 및 설치

sip

우선 터미널에서 아래와 같이 sip를 빌드하고 설치한다.

$ export MACOSX_DEPLOYMENT_TARGET=10.6
$ python configure.py --universal --arch=i386 --arch=x86_64 -s MacOSX10.6.sdk
$ make
$ sudo make install

PyQt 빌드

$ export QTDIR=/Developer/Applications/Qt
$ python configure.py --confirm-license --use-arch=i386 --use-arch=x86_64
$ make
$ sudo make install

테스트

아래와 같은 python 코드를 저장하고 실행해본다

import sys
from PyQt4 import QtCore, QtGui


def translate(widgetname, defstr):
    return QtGui.QApplication.translate(widgetname, defstr)


class Sample(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Sample, self).__init__(parent)

        self.setWindowTitle(translate(u'mainwindow', u'Sample'))
        self.resize(250, 150)
        self.setCenter()

        self.statusBar().showMessage(translate(u'mainwindow', u'Ready'))

        workarea = QtGui.QWidget(self)

        quit = QtGui.QPushButton(translate(u'mainwindow', u'Close'), workarea)
        workarea.connect(quit, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT('close()'))
        quit.setToolTip(translate(u'mainwindow', u'This is a close button.'))
        QtGui.QToolTip.setFont(QtGui.QFont(u'Tahoma', 10))

        hbox = QtGui.QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(quit)

        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)
        workarea.setLayout(vbox)
        self.setCentralWidget(workarea)

    def closeEvent(self, event):
        reply = QtGui.QMessageBox.question(
            self,
            translate(u'messagebox', u'Question'),
            translate(u'messagebox', u'Are you sure to quit?'),
            QtGui.QMessageBox.Yes,
            QtGui.QMessageBox.No)

        if QtGui.QMessageBox.Yes == reply:
            event.accept()
        else:
            event.ignore()

    def setCenter(self):
        screen = QtGui.QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    main = Sample()
    main.show()
    sys.exit(app.exec_())

실행하면 아래와 같은 화면이 표시된다.

2010년 5월 6일

Debian/Opera에서 flash 안에 있는 버튼등이 클릭되지 않을 때

Debian에서 Opera를 사용하는 경우 flash 안에 있는 버튼이 클릭되지 않을 때가 있다. 이때 아래와 같은 방법으로 해결할 수 있다.

  • compiz를 사용하지 않는다.
  • Ctrl-A를 눌러서 페이지 전체를 선택한다.
  • GDK_NATIVE_WINDOWS 환경 변수를 설정한다.

compiz을 사용하지 않으면 3D 화면 효과를 사용할 수 없고, 페이지 전체를 선택하는 방법은 매번 귀찮게 키보드를 누르기 때문에 GDK_NATIVE_WINDOWS환경 변수를 설정하는 것이 편리하다.

아래 명령으로 /usr/lib/opera/operapluginwrapper파일을 편집한다.

$ sudo vi operapluginwrapper

파일을 열고 맨 아래 부분을 아래처럼 고쳐서 operapluginwrapper 가 실행하기 전 환경 변수를 설정한다.

export GDK_NATIVE_WINDOWS=1
exec "$wrapper" "$@"

2010년 4월 30일

OS/X - Opera 10.5x에서 바람 입력기로 한글 입력이 되지 않을때

Opera 10.5x 에서 바람 입력기로 한글 입력이 되지 않을 수 있다. 바람 입력기 설정 화면에서 input by 값을 word로 설정하면 된다.

Opera/addEventListener('load')가 동작하지 않을 때

Opera 10.5x 버전부터 UserScript에서 아래와 같이 load 이벤트에 대해서 함수 연결이 되지 않는다.

document.addEventListener('load', function() {
	alert(1);
}, false);

아래와 같이 그냥 실행하도록 수정한다.

(function () {
	alert(1);
})();

2010년 4월 16일

Opera/Youtube에서 flash를 업데이트 하라고 표시할 때.

출처 : http://www.google.com/support/forum/p/youtube/thread?tid=46dd241356fbad51&hl=en

최신 flash player를 설치했음에도 불구하고 Opera로 Youtube를 접속하면 flash를 업데이트하라는 메시지가 표시된다. 이때는 user script 폴더에 YoutubeProtectionRemover.js파일을 만들어 주면 된다.

// ==UserScript==
// @name        YoutubeProtectionRemover
// @include     http://www.youtube.com/*
// @description Removes lame protection on YouTube
// @copyright 2010, Snap
// ==/UserScript==

window.opera.addEventListener('BeforeScript', function (ev){
	ev.element.text = ev.element.text.replace("yt.flash.update(swfConfig, forceUpdate);","");
}, false);

2010년 4월 13일

Debian/Opera 창을 shade한 후 키보드 입력이 되지 않을 때

Debian에서 iBus를 사용할 경우 Opera 창을 shade하면 키보드 입력이 되지 않을 때가 있다. 이때는 아래 그림에서 표시한 iBus 설정을 바꿔주면 된다(checked 상태면 uncheck로, unchecked 면 check로 해주면 된다).

2010년 4월 11일

Mac OS X keyboard shortcuts

백업용으로 보관. 원문은 http://www.silvermac.com/mac-os-x-keyboard-shortcuts/.

Start up hot-keys (press key while booting)

shortcutdescription
XForce Mac OS X startup
CStart up from a bootable CD
NAttempt to start up from a compatible network server (NetBoot)
TStart up in FireWire Target Disk mode
Opt-Cmd-Shift-DeleteSeeks a different startup volume
ShiftStart up in Safe Boot mode
Cmd-VStart up in Verbose mode
Cmd-SStart up in Single-User mode
Cmd-Opt-EscForce Quit menu
Cmd-Opt-DShow/hide the dock
Ctrl-EjectBring up Shutdown/Sleep/Logout Dialog
Ctrl-Cmd-EjectRestart immediately

Clipboard

shortcutdescription
Cmd-CCopy
Cmd-VPaste
Cmd-XCut
Cmd-ASelect All

Finder Navigation

shortcutdescription
Cmd-NNew Finder window
Cmd-WClose Window
Opt-Cmd-WClose all Windows
Cmd-DownOpen File/Folder
Cmd-UpNavigate to parent
Cmd-RightExpand folder (in list view)
Cmd-LeftCollapse Folder (in list view)
Opt-Cmd-RightExpand folder and its subfolders (in list view)
Opt-Cmd-UpOpen parent folder and close current window
Cmd-Shift-HJump to your Home folder
Cmd-Shift-AJump to your Applications folder
Cmd-Shift UJump to your Utilities folder
Cmd-Shift KJump to the Network browser
Cmd-Shift GGoto Folder…
Cmd-1View as Icons
Cmd-2View as List
Cmd-3View as Columns
Cmd-SpaceOpen Spotlight (OS X 10.4)
Cmd-DeleteMove file/folder to trash
Cmd-Click window titleDisplay the file path

Menu Commands

shortcutdescription
shortcutdescription
Shift-Cmd-QLog out
Shift-Opt-Cmd-QLog out immediately
Shift-Cmd-DeleteEmpty Trash
Opt-Shift-Cmd-DeleteEmpty Trash without dialog
Cmd-HHide window
Opt-Cmd-HHide Others
Cmd-NFile New window
Shift-Cmd-NNew Folder
Cmd-OOpen
Cmd-SSave
Shift-Cmd-SSave as
Cmd-PPrint
Cmd-WClose Window
Opt-Cmd-WClose all Windows
Cmd-IGet Info
Opt-Cmd-IShow Attributes Inspector
Cmd-DDuplicate
Cmd-LMake Alias
Cmd-RShow original
Cmd-TAdd to Favorites
Cmd-DeleteMove to Trash
Cmd-EEject
Cmd-FFind
Cmd-ZUndo
Cmd-BHide Toolbar
Cmd-JShow View Opts
Cmd[ = Go Back
Cmd] = Go Forward
Shift-Cmd-CGo to Computer
Shift-Cmd-HGo to your Home folder
Shift-Cmd-IGo to iDisk
Shift-Cmd-AGo to Applications folder
Shift-Cmd-GGo to Go-To Folder
Cmd-KConnect to Server
Cmd-MMinimize Window
Cmd-?Open Mac Help
Cmd-SpaceOpen Spotlight (OS X 10.4)

Screen capture

shortcutdescription
Cmd-Shift 3Capture the screen to a file
Cmd Ctrl Shift 3Capture the screen to the clipboard
Cmd-Shift 4Select an area to be captured to a file
Cmd Ctrl Shift 4Select an area to be captured to the clipboard
Cmd-Shift 4, then press SpaceCapture entire window

Universal Access

shortcutdescription
Opt-Cmd-* (asterisk)Turn on Zoom
Opt-Cmd-+ (plus)Zoom in
Opt-Cmd? (minus)Zoom out
Cmd-Opt-Ctrl-8Invert Screen colours

Window Management

shortcutdescription
Cmd-WClose window
Cmd-Opt-WClose all windows
Cmd-MMinimise window
Cmd-Opt-MMinimise all windows
Cmd-HHide Application
Cmd-~Cycle through windows for current application
Ctrl-F4Cycle through every open window
Cmd-TabBring up the Application Switcher.
Cmd-TabCycle Forwards
Cmd-~Cycle Backwards
Cmd-Shift-TabCycle Backwards
Cmd-QQuit application

Expose

shortcutdescription
F9Show all open windows on the screen at once
F10Show all windows for the current application.
F11Shows Desktop
F12Activates Dashboard
F12 (hold)ejects disk

Miscellaneous

shortcutdescription
Opt-Cmd-DShow/Hide Dock
Ctrl-UpMove up one page
Ctrl-DownMove down one page
Opt-DragCopy to new location
Opt-Cmd-DragMake alias in new location
Cmd-DragMove to new location without copying
Opt-Cmd-EjectSleep
Cmd-click window toolbar button (upper right corner)Cycle through toolbar views
Ctrl-Cmd-D and mouse over word in Cocoa applicationsshows Dictionary description for that word

2010년 4월 7일

Debian/USB 장치가 두 번 mount 되는 문제

usb device를 연결했을 때 아래와 같이 동일한 장치에 대해서 두 번 mount 되는 경우가 있다.

$ mount
/dev/sdg1 on /media/MARKBOY_IPO type vfat (rw,nosuid,nodev,uhelper=udisks,uid=1000,gid=1000,shortname=mixed,dmask=0077,utf8=1,flush)
/dev/sdg1 on /media/MARKBOY_IPO-1 type vfat (rw,nosuid,nodev,sync,uhelper=hal,umask=002)

gnome-volume-managerhalevt가 동시에 설치되었을 때 발생하는 문제인데, halevt 패키지를 삭제하면 된다.

$ sudo apt-get remove halevt

2010년 3월 8일

Debian/PulseAudio와 Flash의 충돌 문제

PulseAudio를 audio server로 사용할 때 PulseAudio를 사용하는 다른 응용프로그램(리듬 박스등)이 실행 중일 때 flash가 동작하지 않거나 소리가 들리지 않는 증상이 있다(그 반대의 증상도 나타난다).

아래와 같은 설정을 추가하면 문제가 해결된다.

아래와 같은 내용으로 ~/.asoundrc파일을 만든다.

pcm.pulse { type pulse }
ctl.pulse { type pulse }
pcm.!default { type pulse }
ctl.!default { type pulse }

2010년 3월 1일

기온 변화 그래프 2

기상청 홈페이지가 개편되면서 날씨 정보에 대한 xml를 제공하기 시작했습니다. 그러나 기온 변화 그래프에서 필요한 현재 시간 날씨 정보에 대해서는 xml 데이타를 제공하지 않습니다.

기온 변화 그래프를 계속 그리기 위해서 기상청 웹페이지를 해석해서 그래프로 그리는 간단한 python 스크립트를 작성해봤습니다.

이 스크립트는 기상청 페이지를 가져와서 xml로 변환하는 buildwexml.py와 그래프를 그리는 gengraph.py 두 부분으로 나뉘어 있습니다.

이 스크립트는 Debian Linux, python 2.5 환경에서 제작, 테스트 되었습니다.

코드는 아래와 같습니다

gengraph.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""gengraph.py

buildwexml.py를 이용해 생성한 xml 파일을 가지고 그래프를 그리는 스크립트
"""
__author__ = 'Yun-yong Choi'
__version__ = '0.1'

import time
import xml.etree.ElementTree

import Gnuplot, Gnuplot.funcutils

import buildwexml

# 데이타를 저장할 경로
write_root = '/tmp/'
# 그래프 파일 경로
graph_filename = '/home/markboy/temperature_graph.png'
writeinfo = {
      u'서울':[u'seoul.dat', u'Seoul'],
      u'부산':[u'busan.dat', u'Busan'],
      u'제주':[u'jeju.dat', u'Jeju'],
}
findcities = [ u'서울', u'부산', u'제주' ]

# web에서 xml 정보를 가져온다.
buildwexml.build(write_root + 'current.xml')

#xml을 해석한다.
xmlobj = xml.etree.ElementTree.parse(write_root + 'current.xml')
timestr = time.strftime('%Y/%m/%d %H', 
                time.strptime(xmlobj.getiterator('datetime')[0].attrib['data'], '%Y.%m.%d.%H:%S') )
# city node를 순회한다.
for city in  xmlobj.getiterator('city'):
    # 찾는 도시가 있으면
    if city.attrib['name'] in findcities :
        # 기온값을 저장한다.
        f = file(write_root + writeinfo[city.attrib['name']][0], 'at')
        f.write("%s %s\n" % (timestr, city.find('temperature').find('now').attrib['data']))
        f.close()
# 일주일분만 표시하기 위해서 7일전 날짜를 구한다.
ago = time.time() - 60 * 60 * 24 * 7
ago_str = time.strftime("%Y/%m/%d", time.localtime(ago) )

# plot 파일 저장
f = file(write_root + 'temperature_graph.plot', 'wt')
f.write("""
set term png small
set size 1.0, 0.6
set output '%s'
set grid
set key left bottom
set ylabel \"Temperature(C)\"

set xdata time
set timefmt \"%%Y/%%m/%%d\"
set xrange [\"%s\":]
set timefmt \"%%Y/%%m/%%d %%H\"
set format x \"%%m/%%d\"

plot """ % (graph_filename, ago_str) )

for key in writeinfo.keys() :
    f.write("'%s' using 1:3 title \"%s\" with line" % (write_root + writeinfo[key][0], writeinfo[key][1]))
    if key <> writeinfo.keys()[-1] :
        f.write(', ')
f.close()
# 그래프 그리기
g = Gnuplot.Gnuplot()
g.load(write_root + 'temperature_graph.plot')

buildwexml.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" buildwexml.py

기상청의 현재 날씨 정보 페이지를 가져와서 xml 파일을 만드는 스크립트
"""
__author__ = 'Yun-yong Choi'
__version__ = '0.2'

import os
import sys
import codecs
import urllib2

def getWebpage(url, referer='') :
    """url 파일을 읽어온다"""
    debug = 0
    if debug :
        return file(url.split('/')[-1], 'rt').read()
    else :
        opener = urllib2.build_opener()
        opener.addheaders = [
            ('User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)'),
            ('Referer', referer),
        ]
        return opener.open(url).read()

def getMainPage():
    return getWebpage('http://www.kma.go.kr/weather/observation/currentweather.jsp')

def getDataPage():
    return getWebpage('http://www.kma.go.kr/weather/observation/currentweather_data.jsp', 'http://www.kma.go.kr/weather/observation/currentweather.jsp' )

def normalize(s):
    """<td> tag와  를 제거한 문자열을 돌려준다.
    
        s - 입력 문자열
    """
    return s.replace('<td>', '').replace('</td>', '').replace(' ', '')

def printUsing():
    """사용 방법을 출력한다"""
    print sys.argv[0], '<output file name>'
    
def getDateTime(buffers):
    """ html 내용을 해석해서 데아타가 생성된 날짜를 얻는다. 날짜 형식은 yyyy.mm.dd.HH:SS 이다.
    
        buffers - html 파일 내용
    """
    return buffers.split('<p class="table_topinfo">')[1].split('</p>')[0].split('/>')[-1]

    
def getDatablocks(buffers):
    """html 내용을 해석해서 도시별로 묶은 list를 돌려준다
    
        buffers - html 파일 내용
    """
    # <table class="table_develop 앞부분을 잘라낸다.
    a = buffers.split('<table class="table_develop"')[1]
    # 맨 처음 만나는 </table>을 기준으로 뒤를 잘라낸다.
    b = a.split('</table>')[0]
    # </thread>를 기준으로 앞을 잘라낸다.
    c = b.split('</thead>')[1].replace('<tr>', '')
    # 빈 줄 제거
    r = ''
    for line in c.split('\n') :
        line = line.decode('cp949').encode('utf-8')
        line = line.strip()
        if len(line) > 0 :
            r = r + line + '\n'
    # </tr> 기준으로 잘라내면 데이타 block이 완성된다.
    return r.split('</tr>\n')[:-1] # 마지막 block은 버린다.

def writeXMLheader(out):
    """XML header를 기록한다."""
    out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    out.write('<xml>\n')
    out.write('  <current_weather>\n')
    
def writeXMLfooter(out):
    """XML footer를 기록한다."""
    out.write('  </current_weather>')
    out.write('</xml>')
    
def writeXMLnode(out, datablock):
    data = datablock.split('\n') # 0번째 <tr>부분은 버린다.
    # 0 - 이름
    city = data[0].split('</a>')[0].split('>')[2]
    out.write("""    <city name="%s">
      <!-- 날씨 -->
      <weather>
        <!-- 현재일기 -->
        <now data="%s" />
        <!-- 시정(km) -->
        <visibility data="%s" />
        <!-- 운량 (1/10) -->
        <clouds data="%s" />
        <!-- 중하운량 -->
        <ml_clouds data="%s" />
      </weather>
      <!-- 기온 -->
      <temperature>
        <!-- 현재 기온 -->
        <now data="%s" />
        <!-- 이슬점온도 -->
        <dew_point_c data="%s" />
        <!-- 체감 온도 -->
        <wind_chill_c data="%s" />
      </temperature>
      <!-- 강수 -->
      <precipitation>
        <!-- 일강수(mm) -->
        <now data="%s" />
        <!-- 적설 (cm) -->
        <snow_cover data="%s" />
        <!-- 습도 (%%) -->
        <humidity data="%s" />
      </precipitation>
      <!-- 바람 -->
      <wind>
        <!-- 풍향 -->
        <direction data="%s" />
        <!-- 풍속(m/sec) -->
        <speed data="%s" />
      </wind>
      <!-- 기압 (hPa) -->
      <atmospheric_pressure>
        <!-- 해면 기압 -->
        <see_level data="%s" />
      </atmospheric_pressure>
    </city>
""" % (city,
    normalize(data[1]),
    normalize(data[2]),
    normalize(data[3]),
    normalize(data[4]),
    normalize(data[5]),
    normalize(data[6]),
    normalize(data[7]),
    normalize(data[8]),
    normalize(data[9]),
    normalize(data[10]),
    normalize(data[11]),
    normalize(data[12]),
    normalize(data[13]) ) )

def build(outputname) :
    """ 기상청 사이트에서 현재 날씨 정보를 읽어와서 xml로 저장한다.

        outputname - 저장할 파일 이름
    """
    out = file(outputname, 'wt')
    writeXMLheader(out)
    out.write('    <datetime data="%s" />\n' % getDateTime(getMainPage()))

    for datablock in getDatablocks(getDataPage()) :
        writeXMLnode(out, datablock)
    writeXMLfooter(out)

if __name__ == '__main__' :
    if len(sys.argv) <> 2 :
        printUsing()
        sys.exit(1)
    build(sys.argv[1])

2010년 2월 28일

Opera/다음 TV 팟 화면 정리하기

맥북에서 다음 TV 팟을 보려고 하면 미묘한 화면 길이 때문에 약간의 스크롤을 필요로 한다. 또한 덧글의 수준이 매우 낮은데 기본으로 덧글을 표시해서 귀찮다.

내가 보기 싫은 element를 숨기도록 user script를 작성했다.

// ==UserScript==
// @name tvpot.user.js
// @author	Yun-yong, Choi
// @version	0.1
// @include	http://tvpot.daum.net/*
// @compatible   Opera 9
// @description  Macbook 에서 TV pot을 볼때 페이지 길이를 줄이기 위해서 상단의 일부 메뉴와 덧글을 숨기는 script.
// ==/UserScript==
(function  () {
	function getElementsByClass( searchClass, domNode, tagName) {
		if (domNode == null) domNode = document;
		if (tagName == null) tagName = '*';
		var el = new Array();
		var tags = domNode.getElementsByTagName(tagName);
		var tcl = " "+searchClass+" ";
		for(i=0,j=0; i<tags.length; i++) {
			var test = " " + tags[i].className + " ";
			if (test.indexOf(tcl) != -1) el[j++] = tags[i];
		}
		return el;
	}
	function hideClass(className) {
		var els = getElementsByClass(className);
		for (var i=0; i < els.length; i++) els[i].style.display = "none";
	}
	function hideId(id) {
		var els = document.getElementById(id);
		if (els) els.style.display = "none";
	}

	///// 화면 상단
	// 최상위 메뉴 숨김
	hideId("DaumUI__minidaum");
	// TV Pot 로고 제거
	hideId("gnbLogoNav");
	// TV pot 메뉴 제거
	//hideId("gnbTabNavNew");
	// title, 브랜드팟 이동 메뉴 모두 제거
	hideClass("brandPotHeadWrap");
	// title 스킨 제거
		//hideClass("header");
	// 브랜드 팟 이동 메뉴 제거
		//hideClass("brandPotNav");
	///// 화면 하단
	// 통계 제거
	hideId("statisticsArea");
	// 추천 메뉴 제거
	hideClass("etcClipInfor");
	// 마이팟 담기, 통계 메뉴 제거
	hideClass("clipEtcFucntion");
	// 댓글 제거
	hideClass("commentArea");
	// 브랫드 팟 랭킹 목록 제거
	hideClass("brandPotRankingList");

	// 동영상을 화면 상단에 표시하기 위해서 화면을 scroll
	// @todo 계산을 해서 scroll 하도록 수정해야 한다.
	scrollTo(0, 100);
})();

2010년 1월 22일

Debian/Eclipse 실행 시 오류 수정

debian에서 eclipse 실행 시 오류가 발생할 때 수정하는 방법을 기술한다.

Integrated browser support not working

실행 시 아래와 같이 Integrated browser support not working오류 창이 발생 한다.

라이브러리가 설치되지 않아서 발생한 문제이다. 임시로 icedove 패키지를 설치하고 라이브러리를 링크한다.

$ sudo apt-get install icedove
$ $ sudo ln -s /usr/lib/icedove/libgtkembedmoz.so /usr/lib/xulrunner/libgtkembedmoz.so

eclipse 오류 창 발생

실행 시 같은 오류 창이 발생한다. 이때는 로그 파일을 읽어서 원인을 찾아야 한다.

Welcome screen 표시 오류

로그 내용이 아래와 비슷하면, Welcome screen 표시 못하는 경우이다.

!SESSION 2010-01-22 00:28:09.672 -----------------------------------------------
eclipse.buildId=M20080911-1700
java.version=1.6.0_0
java.vendor=Sun Microsystems Inc.
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=ko_KR
Framework arguments:  
Command-line arguments:  -os linux -ws gtk -arch x86_64 

!ENTRY org.eclipse.osgi 4 0 2010-01-22 00:28:14.675
!MESSAGE Application error
!STACK 1
org.eclipse.swt.SWTError: XPCOM error -2147467262
	at org.eclipse.swt.browser.Mozilla.error(Mozilla.java:1638)
	at org.eclipse.swt.browser.Mozilla.setText(Mozilla.java:1861)
	at org.eclipse.swt.browser.Browser.setText(Browser.java:737)
	at org.eclipse.ui.internal.intro.impl.presentations.BrowserIntroPartImplementation.generateContentForPage(BrowserIntroPartImplementation.java:252)
	at org.eclipse.ui.internal.intro.impl.presentations.BrowserIntroPartImplementation.dynamicStandbyStateChanged(BrowserIntroPartImplementation.java:451)
	at org.eclipse.ui.internal.intro.impl.presentations.BrowserIntroPartImplementation.doStandbyStateChanged(BrowserIntroPartImplementation.java:658)
	at org.eclipse.ui.internal.intro.impl.model.AbstractIntroPartImplementation.standbyStateChanged(AbstractIntroPartImplementation.java:249)
	at org.eclipse.ui.internal.intro.impl.model.IntroPartPresentation.standbyStateChanged(IntroPartPresentation.java:443)
	at org.eclipse.ui.intro.config.CustomizableIntroPart.standbyStateChanged(CustomizableIntroPart.java:266)
	at org.eclipse.ui.internal.ViewIntroAdapterPart$2.run(ViewIntroAdapterPart.java:74)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
	at org.eclipse.ui.internal.ViewIntroAdapterPart.setStandby(ViewIntroAdapterPart.java:70)
	at org.eclipse.ui.internal.ViewIntroAdapterPart$1.propertyChanged(ViewIntroAdapterPart.java:55)
	at org.eclipse.ui.internal.WorkbenchPartReference.fireInternalPropertyChange(WorkbenchPartReference.java:374)
	at org.eclipse.ui.internal.WorkbenchPartReference.fireZoomChange(WorkbenchPartReference.java:539)
	at org.eclipse.ui.internal.PartPane.setZoomed(PartPane.java:349)
	at org.eclipse.ui.internal.PartStack.setZoomed(PartStack.java:1526)
	at org.eclipse.ui.internal.PartSashContainer.zoomIn(PartSashContainer.java:884)
	at org.eclipse.ui.internal.PartSashContainer.childRequestZoomIn(PartSashContainer.java:905)
	at org.eclipse.ui.internal.LayoutPart.requestZoomIn(LayoutPart.java:354)
	at org.eclipse.ui.internal.PartStack.setState(PartStack.java:1501)
	at org.eclipse.ui.internal.WorkbenchPage.setState(WorkbenchPage.java:3872)
	at org.eclipse.ui.internal.WorkbenchPage.toggleZoom(WorkbenchPage.java:3944)
	at org.eclipse.ui.internal.WorkbenchIntroManager.setIntroStandby(WorkbenchIntroManager.java:201)
	at org.eclipse.ui.internal.WorkbenchIntroManager.showIntro(WorkbenchIntroManager.java:136)
	at org.eclipse.ui.internal.WorkbenchWindow$20.runWithException(WorkbenchWindow.java:2182)
	at org.eclipse.ui.internal.StartupThreading$StartupRunnable.run(StartupThreading.java:31)
	at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:133)
	at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3378)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3036)
	at org.eclipse.ui.application.WorkbenchAdvisor.openWindows(WorkbenchAdvisor.java:803)
	at org.eclipse.ui.internal.Workbench$25.runWithException(Workbench.java:1361)
	at org.eclipse.ui.internal.StartupThreading$StartupRunnable.run(StartupThreading.java:31)
	at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:133)
	at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3378)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3036)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2293)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2198)
	at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:493)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:288)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:488)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:113)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:193)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:386)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:616)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:549)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:504)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1236)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1212)

아래와 같이 실행한다.

$ eclipse -vmargs -Dorg.eclipse.swt.browser.XULRunnerPath=/usr/lib/xulrunner/

eclipse가 실행되면 Workbench아이콘을 눌러서 Welcome screen을 끄면 다음 실행부터는 오류가 발생하지 않는다.

2010년 1월 12일

Python/사진 파일의 시간을 사진을 찍은 시간으로 변경하기

사진 파일(jpg)의 시간을 사진을 찍은 시간으로 변경하는 python script. EXIF.py를 사용한다.

#!/usr/bin/python
# -*- coding: utf-8 -*-
"""jpg 파일 변경 시간을 exif 에서 읽어온 시간으로 변경한다."""
__version__ = '0.1'

import os
import sys
import time
import dircache

import EXIF

# 허가할 확장자 목록
EXTENSIONS = ('.jpg', '.jpeg')

# 사진을 찍은 시간을 얻을 필드. 처음 발견하는 필드를 사용한다.
TAGS = ['Image DateTime', 'EXIF DateTimeOriginal', 'DateTime']


def printUsing():
    """사용 방법을 출력한다.
    """
    print sys.argv[0], '<file or directory>'


def EXIFDateTime2time(exifdatetime):
    """exif의 시간 문자열을 time 값으로 변경한다.

        @param[in]  exifdatetime    시간 문자열

        @return     변환된 time 값
    """
    return time.mktime(time.strptime(exifdatetime, '%Y:%m:%d %H:%M:%S'))


def correctJPGTime(filename):
    """jpg 파일 변경 시간을 수정한다.

        @param[in]  filename    수정할 파일 이름
    """
    fnl = filename.lower()
    for ext in EXTENSIONS:
        # 파일 확장자 검사
        if fnl.endswith(ext):
            f = file(filename, 'rb')
            # exif tag를 읽는다.
            tags = EXIF.process_file(f, details=False)
            for key in TAGS:
                if key in tags:
                    # TAGS 중 처음 발견한 tag의 시간 값을 사용한다.
                    exif_time = EXIFDateTime2time(str(tags[key]))
                    statinfo = os.stat(filename)
                    if exif_time != statinfo.st_mtime:
                        # 시간이 다르면 파일 시간을 변경한다.
                        os.utime(filename, (statinfo.st_atime, exif_time))
                        print 'fix:', filename
                    break
            break


def main():
    if os.path.isdir(sys.argv[1]):
        # 주어진 인자가 디렉토리면
        for name in dircache.listdir(sys.argv[1]):
            # 디렉토리의 파일들에 대해서 수정 작업을 한다.
            # sub directory에 대해서는 아무 작업도 하지 않는다.
            fullname = sys.argv[1] + os.path.sep + name
            if os.path.isfile(fullname):
                correctJPGTime(fullname)
    elif os.path.isfile(sys.argv[1]):
        # 주어진 인자가 파일이면
        correctJPGTime(sys.argv[1])


if __name__ == '__main__':
    if len(sys.argv) < 2:
        printUsing()
        sys.exit(1)
    main()

2010년 1월 2일

GCC/target platform이 64bit 인지 확인하는 방법

gcc에서 target platform이 64bit 환경인지 확인하기 위해서 아래 매크로들이 정의되었는지 확인하면 된다3.2이상에서 확인할 수 있다검사할 64bit 가 LP64만 존재할 경우 gcc 3.4이상에서는 __LP64__매크로 정의 여부만 확인해도 된다..

__alpha__
__ia64__
__ppc64__
__s390x__
__x86_64__

예제 코드는 아래와 같다

#include <stdio.h>

int main(int argc, char **argv)
{
#if defined(__alpha__) || defined(__ia64__) || defined(__ppc64__) || defined(__s390x__) || defined(__x86_64__)
	printf("64\n");
#else
	printf("32\n");
#endif
}