Sep 10 2008

CVS loginfo(post-commit trigger) 작업

분류: Tip.Tech 태그: ,, , Heart @ 11:45 오후

Trackback : http://dev.heartsavior.net/archives/190/trackback/

(프로젝트 중에 CVS 를 연동해야 하는 작업이 있어서, 당분간은 CVS 사용 관련 포스팅이 이어질 것 같다.)

CVS 의 저장소에 변경이 일어나는 시점에(commit) 특정 명령을 수행하게 할 수 있다.
자세한 설명은 아래의 post를 참조하고…

@ Letting CVS Pull The Trigger

여러 trigger 가 있지만, 그 중 commit 이후 수행되는 trigger 는 loginfo 이다.

trigger 를 수정하는 방법 중 가장 안전한 것은 저장소의 CVSROOT 모듈을 checkout 하고 수정한 다음 commit 하는 것이다.

포스트를 참고로 해서 loginfo 에 한 줄을 추가해 넣었다.

test/* (/cvs-log/postCommit.sh %p %{sVv} $USER)

test 모듈의 모든 파일을 대상으로 commit 발생 시 /cvs-log/postCommit.sh 를 실행하겠다는 뜻이다.
괄호 안의 내용들은 그대로 shell에서 실행되므로 실행될 argument 문자열을 입맛에 맞게 조절할 수 있다.

아래 인자들은 CVS에 의해 정보로 변환되어 들어간다.

%p : 디렉토리 경로(모듈 경로가 포함됨. ex. test/DirectoryA/)
%{sVv} : s 는 파일명, V 는 기존 버전 번호, v 는 신규 버전 번호
$USER : commit 한 유저 아이디

commit 시에 디렉토리 별로 shell 이 한 번 실행되는 것 같다. 예를 들면 commit 시에 변경 파일들이…

test/DirA/A.txt 1.1 -> 1.2
test/DirA/B.txt 신규(1.1)
test/DirB/C.txt 삭제(기존 1.3)
test/DirB/D.txt 1.3 -> 1.4

이라면 shell 은 이렇게 실행되었다.

/cvs-log/postCommit.sh test/DirA/ A.txt 1.1 1.2 B.txt NONE 1.1 cvsuser
/cvs-log/postCommit.sh test/DirB/ C.txt 1.3 NONE D.txt 1.3 1.4 cvsuser

예외 사항이 있다면, 디렉토리는 ADD 할 때 바로 hook 이 동작한다. 디렉토리 자체가 cvs에서는 commit 으로 추가되는 개체가 아니다.
그리고 파일과 구분하기 위해서, 디렉토리는 파일 명 부분에 ‘-’ 가 한 번 들어온다. (이후에 New, directory 가 더 들어오는데 알아서 걸러내면 된다.) 버전은 NONE NONE 으로 들어오니 이를 통해 구분해도 될 것으로 보인다.

CVS 는 디렉토리를 삭제할 수 없다. 저장소(물리적인 위치) 에서 직접 rm -rf 명령으로 삭제하여야 한다. 그러므로 디렉토리 삭제에 대한 감지는 되지 않는다.

postCommit.sh 자리에는 실행 가능한 어떠한 것이라도 올 수 있다. 여기서는 shell script 를 적었지만 그냥 막바로 프로그램을 돌릴 수도 있다. 링크된 post에서는 cvs 의 commit 이 block되기 때문에 처리가 많이 걸리는 것은 직접 걸어두지 말고 프로세스를 하나 더 두는 것을 제시하고 있다.

작업시 주의해야 할 것은, cvsd 설치 시에 chroot jail 설정을 한 경우에는 loginfo 가 실행하는 디렉토리 경로도 chroot가 적용된다는 것이다. (이것때문에 거의 반나절 가까이 보냈다.)
loginfo 가 실행해 주는 셸 내에서도 chroot가 적용되는 것으로 확인했다. (프로그램도 아마 마찬가지가 아닐까 생각된다.)

위의 예를 들자면, cvsd가 /var/lib/cvsd 에 설치되고 chroot 가 /var/lib/cvsd 로 설정되어 있다면, postCommit.sh 파일은 /var/lib/cvsd/cvs-log/ 에 위치해 있어야 한다.

그리고, sh(shell 실행파일) 이라던지 cat, date 등이 없기 때문에 필요하다면 chroot 의 해당 디렉토리로 복사해 넣어야 한다.

윈도우즈에서만 작업하다가 linux 작업을 하다 보니 chroot 에 대해 전혀 모르고 있다가 된통 당할 뻔했다.

ps. 작업환경은 우분투 OS 에 cvsd 패키지를 설치했고, CVS 버전은 1.12.13 (client/server) 이다.

ps2. received broken pipe signal 에러가 나서 한참 고생했을 때 chroot 문제임을 알게 해 준 포스트

@ cvsd: frequently asked questions


Aug 04 2008

ASP로 AJAX & Cross Domain 해결하기

분류: Tip.Tech 태그: ,, , Heart @ 10:34 오후

Trackback : http://dev.heartsavior.net/archives/170/trackback/

우선 AJAX 개념이 필요하다면 아래 포스트들을 참조…

@ Head Rush Ajax / 동기 & 비동기 + AJAX 기본 흐름
@ ASP + AJAX Step 1 / Step 2 / Step 3

ASP 사이트에서 IIS 외의 웹 서버에서 AJAX 요청을 수행하려고 했지만, Cross Domain 정책에 막혀 실패했다.

구글링을 열심히 해 본 결과, 몇 가지 방법을 찾을 수 있었다. (방법들이 깔끔하게 요약된 포스트가 있어서 링크한다.)

@ Cross Domain Ajax: a Quick Summary

그 중에 Web Proxy 를 사용하기로 결정!
가장 쉬워보였고, 무엇보다 야후 개발자 네트워크에 설명되어 있어서…;;
야후 OpenAPI 를 이런 식으로 사용하길 권장하는 것이니 괜찮은 방법일 것으로 보인다.

@ Use a Web Proxy for Cross-Domain XMLHttpRequest Calls - Yahoo! Developer Network

위의 포스트에는 PHP 로 proxy 를 만들어서 쓰고 있는데, ASP 사이트이기 때문에 ASP 로 proxy 를 만들어 보려 했지만 결국 실력이 딸려 프로그래밍 갤러리에 약간의 도움을 받고(도움 요청글) 코드를 정리해서 완성했다.
(JSP로는 껌일 것 같은데 ASP는 전혀 모르겠다… ‘어플 프로그래머니까…’ 라는 핑계를…-_-;; )

먼저 내가 갖고 있었던, Cross-Domain의 문제를 가지고 있던 코드를 내부 페이지를 호출하도록 변경…

<html>
	<head>
		<title>AJAX TEST - FROM SimpleHttpServer</title>
 
		<script>
			function newXMLHttpRequest() {
				var xmlreq = false;
 
				// IE가 아닌 브라우저
				if(window.XMLHttpRequest) {
					xmlreq = new XMLHttpRequest();
 
				} 
				// IE
				else if(window.ActiveXObject) {
					try {
						// 최근 IE
						xmlreq = new ActiveXObject("Msxml2.XMLHTTP");
					} catch(e1) {
						try {
							// 예전 IE
							xmlreq = new ActiveXObject("Microsoft.XMLHTTP");
 
						} catch(e2) {
							// ALL FAILED !!
						}
					}
				}
 
				return xmlreq;
			}
 
			function getNewID() {
				var req = newXMLHttpRequest();
 
				// CALLBACK 메소드로 getReadyStateHandler를 등록
				var handlerFunction = getReadyStateHandler(req, showNewID);
				req.onreadystatechange = handlerFunction;
 
				// 첫번째 파라미터 - 방식(GET or POST)
				// 두번째 파라미터 - 주소
				// 세 번째 파라미터 - 비동기 여부(true가 비동기, false는 동기)
				req.open("GET","veoh.asp",true);
 
				// POST 이면 헤더도 바꿈
				/*
				req.setRequestHeader("Content-type", 
					"application/x-www-form-urlencoded");
				*/
 
				// 쿼리 스트링이 있을 경우 파라미터로 작성
				req.send(null);
 
			}
 
			function getReadyStateHandler(req, responseJSONHandler) {
				return function() {
					// AJAX에서 데이터를 완전히 얻어온 상태
					if(req.readyState == 4) {
						// 웹 서버에서 코드 200(성공) 을 리턴하였음
						if(req.status == 200) {
							// TEXT(JSON) DATA 가 req.responseText에 들어 있음
 
							// responseJSONHandler 파라미터로 넘어온 함수를 호출
							responseJSONHandler(req.responseText);
 
						} else {
							alert("HTTP error: " + req.status);
						}
					}
				}
			}
 
			// 실제 처리가 이루어지는 함수
			function showNewID(idJSON) {
				// JSON 문자열이 비었으면 에러
				if( idJSON == null ) {
					alert("idJSON is null!!");
					return;
				}
 
				alert(idJSON);
 
				// 일반 문자열에서 JSON으로 변환
				var jsonObject = eval('(' + idJSON + ')');
 
				// error 프로퍼티를 얻음
				var error = jsonObject.error;
 
				if( error == null ) {
					alert("JSON Parse Failed!!");
					return;
 
				} 
				// 성공할 경우 에러 코드 0, 실패할 경우  > 0을 넣어놓도록 되어 있음
				else if( error > 0 ) {
					alert("ERROR Returned!! Error Code : " + error);
					return;
				}
 
				// ID 프로퍼티를 얻음
				var id = jsonObject.ID;
 
				if( id == null ) {
					alert("JSON Parse Failed!!");
					return;
				} else {
					// 얻어온 ID값을 출력
					alert("ID Received!! ID : " + id);
				}
			}
		</script>
 
	</head>
 
	<body>
	<button onclick="getNewID()">새 아이디 얻기</button>
	</body>
</html>

AJAX로 내부 사이트의 proxy 페이지를 호출하며, proxy 페이지로 veoh.asp 를 사용한다.
veoh.asp 에서 외부 사이트의 내용을 넘겨주기만 하면 된다.
Cross-Domain 을 위한 ASP Proxy 페이지(veoh.asp) 는 아래와 같다.

< % @LANGUAGE="JSCRIPT" CODEPAGE="65001" %>
< %
	// Custom Functions
	function $_GET(values) {
		return Request.QueryString(values)!="undefined"?String(Request.QueryString(values)):null;
	}
 
	function echo(values) {
		Response.Write(values);
	}
 
	// Define Timeout Limit
	Server.ScriptTimeout = 10;
 
	Object.prototype.toParam = function(){
		var querystring = [];
		for (var inc in this) {
			if(!this.hasOwnProperty(inc)) continue;
			if (this[inc]!=null) {
				querystring.push(inc + "=" + Server.URLEncode(String(this[inc])));
			} else {
				querystring.push(inc + "=");
			}
		}
 
		return querystring.join("&");
	}
 
	var xmlHTTP = Server.CreateObject("MSXML2.ServerXMLHTTP");
 
	// 원래 AJAX 요청을 수행하려던 페이지를 호출한다
	xmlHTTP.open("GET", "http://www.example.com/idrequest", false); //, false, false);
 
	xmlHTTP.setRequestHeader("Content-type", "text/xml");
	xmlHTTP.setRequestHeader("Cache-control","no-cache");
 
	xmlHTTP.send();
 
	Response.Write(xmlHTTP.responseText);
 
	xmlHTTP = null;
%>

이렇게 하면 잘 된다. 흐흐… 문제는, 디버깅이 엄청 어려울 것으로 예상된다는거… 서버를 꺼놓고 테스트해보니까 404가 아닌 500 이 리턴됐고, 더 중요한 것은 exception catch 가 안된다 -_-;;

ps. 프로토타입(prototype.js) 으로 Cross Domain 을 해결하는 포스트가 있긴 한데 테스트는 못해봤음.

@ [PrototypeJS] Cross Domain상의 Ajax 호출


Aug 04 2008

VC++ 부동소수점 계산의 정확도를 높이는 방법

분류: Tip.Tech 태그: ,, , , Heart @ 10:48 오전

Trackback : http://dev.heartsavior.net/archives/164/trackback/

프로그래밍 갤러리에서 놀고 있었는데(…) 흥미로운 떡밥 하나가 올라왔다.
나도 모르게 덥썩 물어버렸다(…)

문제의 코드는 이것

#include <stdio .h>
 
int main(void)
{
float radius;
float area;
 
printf("원의 반지름 입력 : ");
scanf("%f", &radius);
 
area = radius*radius*3.1415;
 
printf("원의 넓이 : %f \n", area);
 
return 0;
}
</stdio>

글쓴이 말로는, VC++ 6 과 VS.net 2005 가 계산 결과가 다르게 나온다는 것이었다.

동강꺼랑 코드는 똑같애 12.0 입력했을 경우

내가 나온 값(VS.net 2005)은 452.376007
동강 나온 값(VC++6)은 452.376000

비슷한 문제를 GpgStudy 에서 찾을 수 있었다.
해결 방법 외에도 정확한 설명이 나와 있어서 기반 지식까지 쌓을 수 있었다.(물론 MSDN에 있는 내용… 이라지만 영어보다는 한글 설명이…^^;; 그리고 좀 덜 딱딱한 문체이기도 하고…)

@ assert의 버그?

답만 정리하자면, VC++ 6는 컴파일 옵티마이징 옵션중에 /Op (Improve Float Consistency)를 켜면 되고, VS 2008 은 기본이 /fp:precise (/Op를 대체) 로 되어 있어서 신경쓰지 않아도 된다.

이렇게 하면 위의 코드 실행 결과 VC++ 6 도 452.376007 이 나오게 된다.

자세한 것은 MSDN 자료를 참조하자.
vc++ 6 : http://msdn.microsoft.com/en-us/library/aa984742(VS.71).aspx
VS.net 2005~ : http://msdn.microsoft.com/ko-kr/library/e7s85ffb(VS.80).aspx

ps. 코드 하일라이트가 c/c++ include 태그 부분은 깔끔하지가 못한듯… 내가 잘못 쓴 건가…-_-;;


뒷 쪽 »