제3장 Delphi 인터페이스

내용 목차

3.1. 개요
3.2. 예제 프로그램
3.2.1. 프로그램 구성
3.2.2. 프로그램 특징
3.2.3. 공통 프로그램
3.2.4. 클라이언트 프로그램
3.2.5. 서버 프로그램

본 장은 Delphi 인터페이스에서 사용하는 함수와 예제를 설명한다.

3.1. 개요

Delphi 인터페이스에는 Visual Basic과 비슷한 형태로, 클라이언트 라이브러리에서 제공하는 함수를 호출하기 위한 인터페이스 모듈이 존재한다. 개발자는 인터페이스 모듈을 설치하면 모든 함수를 사용할 수 있다. Delphi는 Power Builder나 Visual Basic과 달리 포인터 개념을 가지고 있고 값의 전달 방법이 C 언어와 비슷하며 파스칼의 문법을 따르고 있기 때문에 기존의 언어에 익숙한 사람이라면 쉽게 접근할 수 있다. 따라서 별도의 매크로나 컴포넌트가 없다.

Delphi 인터페이스 컴포넌트는 다음과 같다.

모듈설명
atmi.pasatmi 함수에 대한 프로토타입 정의 파일이다.
fdl.pas필드키 함수에 대한 프로토타입 정의 파일이다.

참고

atmi 함수와 필드키 함수에 대한 프로토타입 및 기능에 대한 설명은 "Tmax Reference Guide""Tmax Application Development Guide"를 참고하고, 예제 프로그램으로 Delphi 인터페이스 사용법의 설명을 대신한다.

3.2. 예제 프로그램

사원번호를 입력하면 이름과 소속, 부서명 등을 Oracle 데이터베이스에서 찾아 읽어 오는 프로그램이다.

3.2.1. 프로그램 구성

atmi.pas, fdl.pas, TuxSvc.pas를 모듈로 프로젝트에서 추가한다.

  • 공통 프로그램

    프로그램 파일설명
    demo.f필드키 버퍼를 정의한 파일이다.
    tmax라이브러리 파일이다.
  • 클라이언트 프로그램

    프로그램 파일설명
    Atmi.dcuatmi 소스 파일을 컴파일해서 생성되는 object 파일이다.
    Atmi.pasatmi 함수에 대한 프로토타입을 정의한 파일이다.
    EmployeeMgr.bpg프로젝트 그룹 파일이다.
    EmployeeMgr.cfg프로젝트 환경설정 파일이다.
    EmployeeMgr.dofDelphi 옵션 파일이다.
    EmployeeMgr.dpr여러 개의 pas 파일과 dfm 파일의 정보를 가지고 있는 프로젝트 파일이다.
    EmployeeMgr.exeobject 파일을 실행 가능한 파일로 만든 것이다.
    EmployeeMgr.res컴파일된 이진 리소스 파일이다.
    Fdl.dcuFdl 소스 파일을 컴파일해서 생성되는 object 파일이다.
    Fdl.pas필드키 함수에 대한 프로토타입을 정의한 파일이다.
    TuxSvc.pasTuxedo 전환용 소스 파일이다.
    main.dcu메인 소스 파일을 컴파일해서 생기는 object 파일이다.
    main.dfm메인 Form 파일이다.
    main.pas클라이언트 프로그램이다.
  • 서버 프로그램

    프로그램 파일설명
    emp_c.mkMakefile이다.
    emp_c.pc서버 프로그램으로 AIX와 Oracle 9i를 사용한다.
    employee.mTmax 환경설정 파일이다.

3.2.2. 프로그램 특징

  • 클라이언트 프로그램

    기능설명
    Tmax 연결Tmax 사용자 계정과 애플리케이션 정의 유저명을 인자로 연결한다.
    버퍼 유형FIELD KEY 버퍼, 필드키 파일을 fdlc 유틸리티로 컴파일하여 'fdl'파일의 생성이 필요하다.
    통신 유형tpcall()을 이용한 동기 통신, 보내는 버퍼와 받는 버퍼가 같다.
    트랜잭션 여부TMS에서 AutoTransaction을 부여한다.
  • 서버 프로그램

    기능설명
    서비스FDLSELECT, FDLDELETE, FDLUPDATE, FDLINSERT를 작성한다.
    데이터베이스 지정Oracle 데이터베이스를 사용한다. 시스템 구성 파일의 SVRGROUP에 데이터베이스 정보를 지정한다.

3.2.3. 공통 프로그램

DataBase EMP Table

다음은 DB operation을 위한 기본 테이블의 예제이다.

EMPNO        NUMBER                NOT NULL        P1
ENAME        VARCHAR(16)
JOB          VARCHAR(16)
MGR          NUMBER
HIREDATE     DATE
SAL          NUMBER(7,2)
COMM         NUMBER(7,2)
DEPTNO       NUMBER

필드키 버퍼의 정의

다음은 필드키 버퍼를 정의한 파일의 예제이다.

<demo.f>

#For tmax demo employee program
EMPNO           7500                long                -                -
ENAME           7501                string              -                -
JOB             7502                string              -                -
MGR             7503                long                -                -
DATE            7504                string              -                -
SAL             7505                float               -                -
COMM            7506                float               -                -
DEPTNO          7507                long                -                -
        
E_TYPE          9009                long                -                -
E_CODE          9010                long                -                -
E_MSG           9011                string              -                -
E_TMP           9012                long                -                -

Tmax 환경설정

다음은 Tmax 환경설정 파일의 예제이다.

<tmconfig.m>

*DOMAIN
dom1        SHMKEY = 70000,  MAXUSER = 200, MINCLH = 1, MAXCLH = 5,
            TPORTNO = 8888, BLOCKTIME = 200, TXTIME = 200

*NODE
tmax1       TMAXDIR = "/home/tmax",
            APPDIR = "/home/tmax/appbin",
            PATHDIR = "/home/tmax/path",
            TLOGDIR = "/home/tmax/log/tlog",
            SLOGDIR = "/home/tmax/log/slog"
            ULOGDIR = "/home/tmax/log/ulog"

*SVRGROUP
svg1        NODENAME = tmax1, DBNAME = ORACLE, 
            OPENINFO = "ORACLE_XA+Acc=P/scott/tiger+SesTm=60",
            TMSNAME = svg1_tms

*SERVER
emp_c        SVGNAME = svg1, MIN = 1

*SERVICE
FDLSELECT    SVRNAME = emp_c
FDLUPDATE    SVRNAME = emp_c
FDLDELETE    SVRNAME = emp_c
FDLINSERT    SVRNAME = emp_c

3.2.4. 클라이언트 프로그램

메인화면 Form 디자인

다음은 메인화면 Form 디자인 화면이다.

다음은 예제에서 사용할 메인 화면 Form 디자인 구성표이다.

컨트롤 이름컨트롤 종류비고
EmployeeMgrFormFormCaption="사원관리 프로그램"
LabelErrTLabelCaption="오류"
BtnExitTButtonCaption="종료"
BtnInsTButtonCaption="입력"
BtnDelTButtonCaption="삭제"
BtnUdtTButtonCaption="수정"
BtnSelTButtonCaption="조회"
EditNameTEdit 
EditEmpNoTEdit 
EditDeptTEdit 
EditCommTEdit 
EditSalTEdit 
EditDateTEdit 
EditMgrTEdit 
EditJobTEdit 
MListTEdit 
BtnReturnTEdit 

다음은 메인 화면 Form 디자인 화면을 실행하는 예제이다.

<main.pas>

unit main;

interface

uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    StdCtrls;

type
    TEmployeeMgrForm = class(TForm)
       LabelEmpNo: TLabel;
       LabelName: TLabel;
       EditEmpNo: TEdit;
       EditName: TEdit;
       GroupBoxInfo: TGroupBox;
       LabelJob: TLabel;
       LabelMgr: TLabel;
       LabelDate: TLabel;
       LabelSal: TLabel;
       EditJob: TEdit;
       EditMgr: TEdit;
       EditDate: TEdit;
       EditSal: TEdit;
       EditComm: TEdit;
       EditDept: TEdit;
       LabelComm: TLabel;
       LabelDept: TLabel;
       BtnSel: TButton;
       BtnUdt: TButton;
       BtnDel: TButton;
       BtnIns: TButton;
       BtnExit: TButton;
       LabelErr: TLabel;
       MList: TMemo;
       BtnReturn: TButton;
       procedure BtnExitClick(Sender: TObject);
       procedure BtnSelClick(Sender: TObject);
       procedure BtnUdtClick(Sender: TObject);
       procedure BtnDelClick(Sender: TObject);
       procedure BtnInsClick(Sender: TObject);
       procedure tmaxStart();
       procedure BtnReturnClick(Sender: TObject);
       procedure ViewErr(a:Pointer);

       { Private declarations }
  public
       { Public declarations }
  end;

var
    EmployeeMgrForm: TEmployeeMgrForm;

implementation

// Atmi, Fdl 사용
uses Atmi, Fdl;
const BufferSize = 1024;

{$R *.DFM}

// String에 길이값 생략
{$H+}

procedure TEmployeeMgrForm.tmaxStart();

var
    tpinfo: pTPSTART;
    ret: integer;

begin
    // atmi.pas 파일에 정의되어 있습니다.
    // Function tmaxreadenv(a:PChar; b:PChar):Integer; cdecl; external TmaxDLL;
    // 자세한 설명은 Tmax Reference Guide를 참고하십시오.
    ret := tmaxreadenv('C:\tmax.env', 'aix5l389');
    if ret < 0 then begin
        ShowMessage('tmaxreadenv Error');
        Exit;
    end;
    // tpstart시 사용자 정보를 보내기 위한 버퍼 할당
    tpinfo := tpalloc('TPSTART', NIL, 0);

    if tpinfo = Nil then begin
        ShowMessage('tpinfo tpalloc failed,' + StrPas(tpstrerror(gettperrno)));
        tpfree(tpinfo);
        Exit;
    end;
    //
    // 다음과 같이 옵션을 지정할 수 있습니다.
    //
    // 사용자 인증 보안을 위한 사용자 계정
    // tpinfo.usrname := 'tmax';
    // 자발적인 메시지 수신시 사용되는 구별 이름
    // tpinfo.cltname := 'tmax';
    // 자발적인 메시지를 허락
    // tpinfo.flags := TPUNSOL_POLL;

    // Tmax 접속
    ret := tpstart(tpinfo);
    if ret < 0 then begin
        ShowMessage('tpstart failed' + StrPas(tpstrerror(gettperrno)));
        tpfree(tpinfo);
        tpend();
        Exit;
    end;
    // 사용자 정보를 보내기 위한 버퍼 해제
    tpfree(tpinfo);
end;

// 종료 버튼을 눌렀을 때.
procedure TEmployeeMgrForm.BtnExitClick(Sender: TObject);
begin
    tpend();
    close;
end;
 

procedure TEmployeeMgrForm.BtnUdtClick(Sender: TObject);

var sndbuf, revbuf: Pointer;
    ret, empno_l, mgr_l, deptno_l: longint;
    sal_f, comm_f: single;
    job_s, ename_s: pointer;
    rlen: integer;
    date_s: string[100];

begin

    tmaxStart();

    // 입력할 버퍼 할당
    sndbuf := fballoc(1000, 10000);
    if sndbuf = Nil then begin
        ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)));
        fbfree(sndbuf);
        tpend();
        Exit;
    end;

    // 받을 버퍼 할당
    revbuf := fballoc(1000, 10000);
    if revbuf = Nil then begin
        ShowMessage('revbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)));
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    empno_l := StrToInt(EditEmpNo.text);
    fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0);

    ename_s := PChar(EditName.text);
    fbput(sndbuf, fbget_fldkey('ENAME'), ename_s, 0);

    job_s := PChar(EditJob.text);
    fbput(sndbuf, fbget_fldkey('JOB'), job_s, 0);

    mgr_l := StrToInt(EditMgr.text);
    fbput(sndbuf, fbget_fldkey('MGR'), @mgr_l, 0);

    // date_s := PChar(EditDate.text);
    // fbput(sndbuf, fbget_fldkey('DATE'), @date_s, 0);
    date_s := EditDate.text;
    rlen := length(EditDate.text);
    ret := fbchg_tu(sndbuf, fbget_fldkey('DATE'),0 ,@date_s[1], 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'DATE error!!!';
        Exit;
    end;

    sal_f := StrToFloat(EditSal.text);
    fbput(sndbuf, fbget_fldkey('SAL'), @sal_f, 0);

    comm_f := StrToFloat(EditComm.text);
    fbput(sndbuf, fbget_fldkey('COMM'), @comm_f, 0);

    deptno_l := StrToInt(EditDept.text);
    fbput(sndbuf, fbget_fldkey('DEPTNO'), @deptno_l, 0);

    // 트랜잭션 시작
    ret := tx_begin();
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'tx_begin error';
        Exit;
    end;


    // 서비스를 호출
    ret := tpcall('FDLUPDATE', sndbuf, 0, @revbuf, @rlen, 0);
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    ret := tx_commit();
    if ret < 0 then begin
        ShowMessage('tx_commit failed! ' + StrPas(tpstrerror(gettperrno)));
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    // 할당했던 버퍼를 해제
    fbfree(sndbuf);
    fbfree(revbuf);
    tpend();
end;

procedure TEmployeeMgrForm.BtnDelClick(Sender: TObject);
var sndbuf, revbuf: Pointer;
    empno_l, ret: longint;
    rlen: integer;

begin

    tmaxStart();

    // 서버로 보낼 버퍼 할당
    sndbuf := fballoc(1000, 10000);
    if sndbuf = Nil then begin
        ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)));
        tpend();
        Exit;
    end;

    // 서버에서 받을 버퍼 할당
    revbuf := fballoc(1000, 10000);
    if sndbuf = Nil then begin
        ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)));
        tpend();
        Exit;
    end;

    empno_l := StrToInt(EditEmpNo.text);
    fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0);

    // 트랜잭션 시작
    ret := tx_begin();
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'tx_begin error';
        Exit;
    end;

    // 서버에 서비스를 호출
    ret := tpcall('FDLDELETE', sndbuf, 0, @revbuf, @rlen, 0);
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    ret := tx_commit();
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    // 할당했던 버퍼를 해제
    fbfree(sndbuf);
    fbfree(revbuf);

    tpend();
end;

procedure TEmployeeMgrForm.BtnInsClick(Sender: TObject);
var sndbuf, revbuf: Pointer;
    empno_l, mgr_l, deptno_l: longint;
    sal_f, comm_f: single;
    job_s, ename_s, date_s: string[100];
    ret, rlen: integer;

begin

    // tpstart 실행 함수
    tmaxStart();

    // 입력할 버퍼 할당
    LabelErr.Caption := '';
    sndbuf := fballoc(1000,10000);
    if sndbuf = Nil then begin
        LabelErr.Caption := 'sndbuf tpalloc failed, ' 
                            + StrPas(tpstrerror(gettperrno));
        fbfree(sndbuf);
        tpend();
        Exit;
    end;
    revbuf := fballoc(100,1000);
    if revbuf = Nil then begin
        LabelErr.Caption := 'revbuf tpalloc failed, ' 
                            + StrPas(tpstrerror(gettperrno));
        fbfree(revbuf);
        tpend();
        Exit;
    end;


    empno_l := StrToInt(EditEmpNo.text);
    ret := fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0);
    if ret = -1 then begin
        LabelErr.Caption := 'EMPNO error!!!';
        Exit;
    end;

    ename_s := EditName.text;
    rlen := length(EditName.text);
    ret := fbchg_tu(sndbuf, fbget_fldkey('ENAME'),0 ,@ename_s[1], 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'ENAME error!!!';
        Exit;
    end;

    job_s := EditJob.text;
    rlen := length(EditJob.text);
    ret := fbchg_tu(sndbuf, fbget_fldkey('JOB'),0 ,@job_s[1], 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'JOB error!!!';
        Exit;
    end;

    mgr_l := StrToInt(EditMgr.text);
    ret := fbput(sndbuf, fbget_fldkey('MGR'), @mgr_l, 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'MGR error!!!';
        Exit;
    end;

    date_s := EditDate.text;
    rlen := length(EditDate.text);
    ret := fbchg_tu(sndbuf, fbget_fldkey('DATE'),0 ,@date_s[1], 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'DATE error!!!';
        Exit;
    end;

    sal_f := StrToFloat(EditSal.text);
    ret := fbput(sndbuf, fbget_fldkey('SAL'), @sal_f, 0);
    if ret = -1 then begin
       LabelErr.Caption := LabelErr.Caption + 'SAL error!!!';
       Exit;
    end;

    comm_f := StrToFloat(EditComm.text);
    ret := fbput(sndbuf, fbget_fldkey('COMM'), @comm_f, 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'COMM error!!!';
        Exit;
    end;

    deptno_l := StrToInt(EditDept.text);
    ret := fbput(sndbuf, fbget_fldkey('DEPTNO'), @deptno_l, 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'DEPTNO error!!!';
        Exit;
    end;

    // 트랜잭션 시작
    ret := tx_begin();
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'tx_begin error';
        Exit;
    end;

    // 서비스를 호출
    ret := tpcall('FDLINSERT', sndbuf, 0, @revbuf, @rlen, 0);
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    ret := tx_commit();
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    // 할당했던 버퍼를 해제
    fbfree(revbuf);
    fbfree(sndbuf);
    tpend();
end;

procedure TEmployeeMgrForm.BtnReturnClick(Sender: TObject);
begin
    BtnReturn.Visible := false;
    MList.Visible := false;
end;

procedure TEmployeeMgrForm.ViewErr( a:Pointer );
var   typeS: PChar;
      codeS: PChar;
      msgS: PChar;
      tmpS: PChar;
     // Iret: Integer;
begin
    // E_TYPE     9009    long    -   -
    // E_CODE     9010    long    -   -
    // E_MSG      9011    string  -   -
    // E_TMP      9012    long    -   -

    typeS := fbgetvals(a, fbget_fldkey('E_TYPE'), 0);
    codeS := fbgetvals(a, fbget_fldkey('E_CODE'), 0);
    msgS := fbgetvals(a, fbget_fldkey('E_MSG'), 0);
    tmpS := fbgetvals(a, fbget_fldkey('E_TMP'), 0);
    ShowMessage('Error Type : ' + typeS + ' ,Code : ' + codeS +  ' ,Message : 
                ' + msgS + ' ,Tmp : ' + tmpS);
end;

조회 화면

다음은 조회 화면 Form 디자인 화면이다.

다음은 조회 화면 Form 디자인 화면을 실행하는 예제이다.

unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TEmployeeMgrForm = class(TForm)
     LabelEmpNo: TLabel;
     LabelName: TLabel;
     EditEmpNo: TEdit;
     EditName: TEdit;
     GroupBoxInfo: TGroupBox;
     LabelJob: TLabel;
     LabelMgr: TLabel;
     LabelDate: TLabel;
     LabelSal: TLabel;
     EditJob: TEdit;
     EditMgr: TEdit;
     EditDate: TEdit;
     EditSal: TEdit;
     EditComm: TEdit;
     EditDept: TEdit;
     LabelComm: TLabel;
     LabelDept: TLabel;
     BtnSel: TButton;
     BtnUdt: TButton;
     BtnDel: TButton;
     BtnIns: TButton;
     BtnExit: TButton;
     LabelErr: TLabel;
     MList: TMemo;
     BtnReturn: TButton;
     procedure BtnExitClick(Sender: TObject);
     procedure BtnSelClick(Sender: TObject);
     procedure BtnUdtClick(Sender: TObject);
     procedure BtnDelClick(Sender: TObject);
     procedure BtnInsClick(Sender: TObject);
     procedure tmaxStart();
     procedure BtnReturnClick(Sender: TObject);
     procedure ViewErr( a:Pointer );

     { Private declarations }
  public
     { Public declarations }
  end;

var
     EmployeeMgrForm: TEmployeeMgrForm;

implementation

// Atmi, Fdl 사용
uses Atmi, Fdl;
const BufferSize = 1024;

{$R *.DFM}

// String에 길이값 생략
{$H+}

procedure TEmployeeMgrForm.tmaxStart();

var
    tpinfo: pTPSTART;
    ret: integer;

begin
    // atmi.pas 파일에 정의되어 있습니다.
    // Function tmaxreadenv(a:PChar; b:PChar):Integer; cdecl; external TmaxDLL;
    // 자세한 설명은 Tmax Reference Guide를 참고하십시오.
    ret := tmaxreadenv('C:\tmax.env', 'aix5l389');
    if ret < 0 then begin
        ShowMessage('tmaxreadenv Error');
        Exit;
    end;
    // tpstart시 사용자 정보를 보내기 위한 버퍼 할당
    tpinfo := tpalloc('TPSTART', NIL, 0);

    if tpinfo = Nil then begin
        ShowMessage('tpinfo tpalloc failed,' + StrPas(tpstrerror(gettperrno)));
        tpfree(tpinfo);
        Exit;
    end;
    //
    // 다음과 같이 옵션을 지정할 수 있습니다.
    //
    // 사용자 인증 보안을 위한 사용자 계정
    // tpinfo.usrname := 'tmax';
    // 자발적인 메시지 수신시 사용되는 구별 이름
    // tpinfo.cltname := 'tmax';
    // 자발적인 메시지를 허락
    // tpinfo.flags := TPUNSOL_POLL;

    // Tmax 접속
    ret := tpstart(tpinfo);
    if ret < 0 then begin
        ShowMessage('tpstart failed' + StrPas(tpstrerror(gettperrno)));
        tpfree(tpinfo);
        tpend();
        Exit;
    end;
    // 사용자 정보를 보내기 위한 버퍼 해제
    tpfree(tpinfo);
end;

// 종료 버튼을 눌렀을 때.
procedure TEmployeeMgrForm.BtnExitClick(Sender: TObject);
begin
    tpend();
    close;
end;
 

procedure TEmployeeMgrForm.BtnUdtClick(Sender: TObject);

var sndbuf, revbuf: Pointer;
    ret, empno_l, mgr_l, deptno_l: longint;
    sal_f, comm_f: single;
    job_s, ename_s: pointer;
    rlen: integer;
    date_s: string[100];

begin

    tmaxStart();

    // 입력할 버퍼 할당
    sndbuf := fballoc(1000, 10000);
    if sndbuf = Nil then begin
        ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)));
        fbfree(sndbuf);
        tpend();
        Exit;
    end;

    // 받을 버퍼 할당
    revbuf := fballoc(1000, 10000);
    if revbuf = Nil then begin
        ShowMessage('revbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)));
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    empno_l := StrToInt(EditEmpNo.text);
    fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0);

    ename_s := PChar(EditName.text);
    fbput(sndbuf, fbget_fldkey('ENAME'), ename_s, 0);

    job_s := PChar(EditJob.text);
    fbput(sndbuf, fbget_fldkey('JOB'), job_s, 0);

    mgr_l := StrToInt(EditMgr.text);
    fbput(sndbuf, fbget_fldkey('MGR'), @mgr_l, 0);

    // date_s := PChar(EditDate.text);
    // fbput(sndbuf, fbget_fldkey('DATE'), @date_s, 0);
    date_s := EditDate.text;
    rlen := length(EditDate.text);
    ret := fbchg_tu(sndbuf, fbget_fldkey('DATE'),0 ,@date_s[1], 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'DATE error!!!';
        Exit;
    end;

    sal_f := StrToFloat(EditSal.text);
    fbput(sndbuf, fbget_fldkey('SAL'), @sal_f, 0);

    comm_f := StrToFloat(EditComm.text);
    fbput(sndbuf, fbget_fldkey('COMM'), @comm_f, 0);

    deptno_l := StrToInt(EditDept.text);
    fbput(sndbuf, fbget_fldkey('DEPTNO'), @deptno_l, 0);

    // 트랜잭션 시작
    ret := tx_begin();
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'tx_begin error';
        Exit;
    end;


    // 서비스를 호출
    ret := tpcall('FDLUPDATE', sndbuf, 0, @revbuf, @rlen, 0);
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    ret := tx_commit();
    if ret < 0 then begin
        ShowMessage('tx_commit failed! ' + StrPas(tpstrerror(gettperrno)));
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    // 할당했던 버퍼를 해제
    fbfree(sndbuf);
    fbfree(revbuf);
    tpend();
end;

procedure TEmployeeMgrForm.BtnDelClick(Sender: TObject);
var sndbuf, revbuf: Pointer;
    empno_l, ret: longint;
    rlen: integer;

begin

    tmaxStart();

    // 서버로 보낼 버퍼 할당
    sndbuf := fballoc(1000, 10000);
    if sndbuf = Nil then begin
        ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)));
        tpend();
        Exit;
    end;

    // 서버에서 받을 버퍼 할당
    revbuf := fballoc(1000, 10000);
    if sndbuf = Nil then begin
        ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)));
        tpend();
        Exit;
    end;

    empno_l := StrToInt(EditEmpNo.text);
    fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0);

    // 트랜잭션 시작
    ret := tx_begin();
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'tx_begin error';
        Exit;
    end;

    // 서버에 서비스를 호출
    ret := tpcall('FDLDELETE', sndbuf, 0, @revbuf, @rlen, 0);
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    ret := tx_commit();
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    // 할당했던 버퍼를 해제
    fbfree(sndbuf);
    fbfree(revbuf);

    tpend();
end;

procedure TEmployeeMgrForm.BtnInsClick(Sender: TObject);
var sndbuf, revbuf: Pointer;
    empno_l, mgr_l, deptno_l: longint;
    sal_f, comm_f: single;
    job_s, ename_s, date_s: string[100];
    ret, rlen: integer;

begin

    // tpstart 실행 함수
    tmaxStart();

    // 입력할 버퍼 할당
    LabelErr.Caption := '';
    sndbuf := fballoc(1000,10000);
    if sndbuf = Nil then begin
        LabelErr.Caption := 'sndbuf tpalloc failed, ' 
                             + StrPas(tpstrerror(gettperrno));
        fbfree(sndbuf);
        tpend();
        Exit;
    end;
    revbuf := fballoc(100,1000);
    if revbuf = Nil then begin
        LabelErr.Caption := 'revbuf tpalloc failed, ' 
                             + StrPas(tpstrerror(gettperrno));
        fbfree(revbuf);
        tpend();
        Exit;
    end;


    empno_l := StrToInt(EditEmpNo.text);
    ret := fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0);
    if ret = -1 then begin
        LabelErr.Caption := 'EMPNO error!!!';
        Exit;
    end;

    ename_s := EditName.text;
    rlen := length(EditName.text);
    ret := fbchg_tu(sndbuf, fbget_fldkey('ENAME'),0 ,@ename_s[1], 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'ENAME error!!!';
        Exit;
    end;

    job_s := EditJob.text;
    rlen := length(EditJob.text);
    ret := fbchg_tu(sndbuf, fbget_fldkey('JOB'),0 ,@job_s[1], 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'JOB error!!!';
        Exit;
    end;

    mgr_l := StrToInt(EditMgr.text);
    ret := fbput(sndbuf, fbget_fldkey('MGR'), @mgr_l, 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'MGR error!!!';
        Exit;
    end;

    date_s := EditDate.text;
    rlen := length(EditDate.text);
    ret := fbchg_tu(sndbuf, fbget_fldkey('DATE'),0 ,@date_s[1], 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'DATE error!!!';
        Exit;
    end;

    sal_f := StrToFloat(EditSal.text);
    ret := fbput(sndbuf, fbget_fldkey('SAL'), @sal_f, 0);
    if ret = -1 then begin
       LabelErr.Caption := LabelErr.Caption + 'SAL error!!!';
       Exit;
    end;

    comm_f := StrToFloat(EditComm.text);
    ret := fbput(sndbuf, fbget_fldkey('COMM'), @comm_f, 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'COMM error!!!';
        Exit;
    end;

    deptno_l := StrToInt(EditDept.text);
    ret := fbput(sndbuf, fbget_fldkey('DEPTNO'), @deptno_l, 0);
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'DEPTNO error!!!';
        Exit;
    end;

    // 트랜잭션 시작
    ret := tx_begin();
    if ret = -1 then begin
        LabelErr.Caption := LabelErr.Caption + 'tx_begin error';
        Exit;
    end;

    // 서비스를 호출
    ret := tpcall('FDLINSERT', sndbuf, 0, @revbuf, @rlen, 0);
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    ret := tx_commit();
    if ret < 0 then begin
        ViewErr(revbuf);
        tx_rollback();
        fbfree(sndbuf);
        fbfree(revbuf);
        tpend();
        Exit;
    end;

    // 할당했던 버퍼를 해제
    fbfree(revbuf);
    fbfree(sndbuf);
    tpend();
end;

procedure TEmployeeMgrForm.BtnReturnClick(Sender: TObject);
begin
    BtnReturn.Visible := false;
    MList.Visible := false;
end;

procedure TEmployeeMgrForm.ViewErr( a:Pointer );
var  typeS: PChar;
     codeS: PChar;
     msgS: PChar;
     tmpS: PChar;
     // Iret: Integer;
begin
    // E_TYPE          9009    long    -   -
    // E_CODE          9010    long    -   -
    // E_MSG           9011    string  -   -
    // E_TMP           9012    long    -   -

    typeS := fbgetvals(a, fbget_fldkey('E_TYPE'), 0);
    codeS := fbgetvals(a, fbget_fldkey('E_CODE'), 0);
    msgS := fbgetvals(a, fbget_fldkey('E_MSG'), 0);
    tmpS := fbgetvals(a, fbget_fldkey('E_TMP'), 0);
    ShowMessage('Error Type : ' + typeS + ' ,Code : ' + codeS +  ' ,Message : ' 
                 + msgS + ' ,Tmp : ' + tmpS);
end;

Oracle 에러 출력 화면

다음은 Oracle 에러 출력 화면이다.

다음은 Oracle 에러 출력 화면을 실행하는 예제이다.

procedure TEmployeeMgrForm.ViewErr( a:Pointer );
var     typeS: PChar;
        codeS: PChar;
        msgS: PChar;
        tmpS: PChar;
        // Iret: Integer;
begin
    // E_TYPE      9009    long    -   -
    // E_CODE      9010    long    -   -
    // E_MSG       9011    string  -   -
    // E_TMP       9012    long    -   -

    typeS := fbgetvals(a, fbget_fldkey('E_TYPE'), 0);
    codeS := fbgetvals(a, fbget_fldkey('E_CODE'), 0);
    msgS := fbgetvals(a, fbget_fldkey('E_MSG'), 0);
    tmpS := fbgetvals(a, fbget_fldkey('E_TMP'), 0);
    ShowMessage('Error Type : ' + typeS + ' ,Code : ' + codeS +  ' ,Message : 
                ' + msgS + ' ,Tmp : ' + tmpS);
end;

3.2.5. 서버 프로그램

서비스 프로그램

다음은 서비스 프로그램의 예제이다.

<em4p_c.pc>

#include <stdio.h>
#include <ctype.h>
#include <tuxinc/macro.h>
#include "../../fdl/demo_fdl.h"

EXEC SQL include sqlca.h;
EXEC SQL INCLUDE ORACA;
EXEC ORACLE   OPTION (ORACA=YES);
EXEC ORACLE   OPTION (RELEASE_CURSOR=YES);

#define INP   1
#define ORA   2
#define TMX   3
#define APP   4

EXEC SQL begin declare section;
int  h_empno;
char h_ename[11];
char h_job[10];
int  h_mgr;
char h_date[11];
float h_sal;
float h_comm;
int  h_deptno;
EXEC SQL end declare section;

void svc_error(long type, long err_code, char *msg, long tmp);

FDLINSERT( TPSVCINFO *msg )
{
        FBUF *rcvbuf;
        int i, occurrence;
        rcvbuf = (FBUF *)msg->data;
        h_empno = h_mgr = h_sal = h_comm = h_deptno = 0;
        
        memset( h_ename, 0x00, sizeof( h_ename ) );
        memset( h_job, 0x00, sizeof( h_job ) );
        memset( h_date, 0x00, sizeof( h_date ) );
        
        occurrence = fbkeyoccur(rcvbuf, EMPNO);
        
        for (i=0; i< occurrence; i++){
             fbget_tu ( rcvbuf, EMPNO, i, (char *)&h_empno, 0 );
             fbget_tu ( rcvbuf, MGR,   i, (char *)&h_mgr, 0 );
             fbget_tu ( rcvbuf, SAL,   i, (char *)&h_sal, 0 );
             fbget_tu ( rcvbuf, COMM,  i, (char *)&h_comm, 0 );
             fbget_tu ( rcvbuf, DEPTNO,i, (char *)&h_deptno, 0 );
             fbget_tu ( rcvbuf, ENAME, i, (char *)h_ename, 0 );
             fbget_tu ( rcvbuf, JOB  , i, (char *)h_job, 0 );
             fbget_tu ( rcvbuf, DATE , i, (char *)h_date, 0 );
                
             EXEC SQL INSERT 
             INTO emp(empno, ename, job, mgr, hiredate, sal,comm, deptno)
             VALUES (:h_empno, :h_ename, :h_job, :h_mgr, 
             to_date(:h_date,'yyyymmdd'), :h_sal, :h_comm, :h_deptno );
        }
        
        if(sqlca.sqlcode != 0)
           goto LB_DBERROR;
        
        EXEC SQL WHENEVER SQLERROR
           goto LB_DBERROR;
        
        tpreturn(TPSUCCESS, 0L, (char *)rcvbuf, 0L, 0L);
        
        LB_DBERROR :
           EXEC SQL WHENEVER SQLERROR CONTINUE;
           svc_error(ORA, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, 0);
}


FDLDELETE( TPSVCINFO *msg )
{
        FBUF *rcvbuf;
        int i, occurrence;

        rcvbuf = ( FBUF *)msg->data;
        occurrence = fbkeyoccur(rcvbuf, EMPNO);

        for (i=0; i< occurrence; i++){
             fbget_tu (rcvbuf, EMPNO, i, (char *)&h_empno , 0);

             EXEC SQL DELETE
             FROM emp
             WHERE empno = :h_empno;
        }        
        if(sqlca.sqlcode != 0)
           goto LB_DBERROR;

        EXEC SQL WHENEVER SQLERROR
           goto LB_DBERROR ;

        EXEC SQL WHENEVER NOT FOUND
           goto LB_DBERROR ;

        tpreturn(TPSUCCESS, 0L, (char *)rcvbuf, 0L, 0L);

        LB_DBERROR :
           EXEC SQL WHENEVER SQLERROR CONTINUE;
           svc_error(ORA, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, 0) ;
}

FDLSELECT( TPSVCINFO *msg )
{
        FBUF *rcvbuf;
        FLDLEN fldlen;
        int i=0, ROWMEM=200, CHKROW=500;

        rcvbuf = (FBUF *)msg->data;

        if((rcvbuf=(FBUF *)tprealloc((char *)rcvbuf,ROWMEM*CHKROW))==NULL){
            rcvbuf=(FBUF *)msg->data;
            goto LB_TMAXERROR ;
        }

        h_empno = h_mgr = h_sal = h_comm = h_deptno = 0;

        memset( h_ename, 0x00, sizeof( h_ename ) );
        memset( h_job, 0x00, sizeof( h_job ) );
        memset( h_date, 0x00, sizeof( h_date ) );

        fbget_tu( rcvbuf, EMPNO, 0, (char *)&h_empno, &fldlen);

        EXEC SQL DECLARE DB_CUR CURSOR FOR
        SELECT  nvl(empno,0), nvl(ename,' '), nvl(job,' '), 
                nvl(to_char(hiredate,'yyyymmdd'),' '),  nvl(mgr,0), 
                nvl(sal,0), nvl(comm,0), nvl(deptno,0)
        FROM  emp
        WHERE empno >= :h_empno-50 AND empno <= :h_empno+50;
        EXEC SQL OPEN DB_CUR;

        if(sqlca.sqlcode != 0)
           goto LB_DBERROR;
        EXEC SQL WHENEVER SQLERROR
           goto LB_DBERROR ;
        EXEC SQL WHENEVER NOT FOUND
           Do break ;

        while(1) {
              EXEC SQL FETCH DB_CUR
              INTO    :h_empno,
                      :h_ename,
                      :h_job,
                      :h_date,
                      :h_mgr,
                      :h_sal,
                      :h_comm,
                      :h_deptno;

                fbchg_tu(rcvbuf, EMPNO, i,(char *)&h_empno, 0);
                fbchg_tu(rcvbuf, MGR, i ,(char *)&h_mgr, 0);
                fbchg_tu(rcvbuf, SAL, i ,(char *)&h_sal, 0);
                fbchg_tu(rcvbuf, DEPTNO, i,(char *)&h_deptno, 0);
                fbchg_tu(rcvbuf, COMM, i,(char *)&h_comm, 0);
                fbchg_tu(rcvbuf, ENAME, i,(char *)h_ename, 0);
                fbchg_tu(rcvbuf, JOB, i,(char *)h_job, 0);
                fbchg_tu(rcvbuf, DATE, i,(char *)h_date, 0);

                i++;
        }

        if (i < 1) 
            goto LB_DBERROR;

        EXEC SQL CLOSE DB_CUR;

        tpreturn(TPSUCCESS, 0L, (char *)rcvbuf, 0L, 0L);

        LB_DBERROR :
            EXEC SQL WHENEVER SQLERROR CONTINUE;
            EXEC SQL CLOSE DB_CUR ;
            svc_error(ORA, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, 0L);

        LB_TMAXERROR :
            EXEC SQL WHENEVER SQLERROR CONTINUE;
            EXEC SQL CLOSE DB_CUR ;
            svc_error(TMX, tperrno, "", 0L) ;
}

FDLUPDATE( TPSVCINFO *msg )
{
        
        FBUF *rcvbuf ;
        int i, occurrence;
        rcvbuf = (FBUF *)msg->data;
        h_empno = h_mgr = h_sal = h_comm = h_deptno = 0;

        memset( h_ename, 0x00, sizeof( h_ename ) );
        memset( h_job, 0x00, sizeof( h_job ) );
        memset( h_date, 0x00, sizeof( h_date ) );

        occurrence = fbkeyoccur(rcvbuf, EMPNO);
        
        for (i=0; i< occurrence; i++){
             fbget_tu ( rcvbuf, EMPNO, i, (char *)&h_empno, 0);
             fbget_tu ( rcvbuf, ENAME, i, (char *)h_ename, 0);
             fbget_tu ( rcvbuf, JOB, i, (char *)h_job, 0);
             fbget_tu ( rcvbuf, MGR, i, (char *)&h_mgr, 0);
             fbget_tu ( rcvbuf, SAL, i, (char *)&h_sal, 0);
             fbget_tu ( rcvbuf, COMM, i, (char *)&h_comm,0);
             fbget_tu ( rcvbuf, DEPTNO, i, (char *)&h_deptno,0);
             fbget_tu ( rcvbuf, DATE , i, (char *)h_date, 0 );

             EXEC SQL UPDATE emp
             SET ename = nvl(:h_ename, ename),
                 job   = nvl(:h_job, job),
                 mgr   = :h_mgr,
                 hiredate  = nvl(to_date(:h_date,'yyyymmdd'),hiredate),
                 sal   = :h_sal,
                 comm  = :h_comm,
                 deptno = :h_deptno
             WHERE empno = :h_empno;
        
             if(sqlca.sqlcode != 0)
                 goto LB_DBERROR;
                
              EXEC SQL WHENEVER SQLERROR
                 goto LB_DBERROR;

              EXEC SQL WHENEVER NOT FOUND
                 goto LB_DBERROR;
        }
              tpreturn(TPSUCCESS, 0L, (char *)rcvbuf, 0L, 0L);

              LB_DBERROR :
                  EXEC SQL WHENEVER SQLERROR CONTINUE;
                  svc_error(ORA, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, 0L);
}

/*********************************************************************
 * 에러처리 :  서비스에서 오류가 발생하면 그 오류를 버퍼에 넣어 클라이언트로 보내준다. 
 ********************************************************************/
void svc_error(long type, long err_code, char *msg, long tmp) {
        FBUF *transf;
        char *svcname;
        char err_msg[100];
        char temp[100];
        int i = 0;

        printf("type     ==>[%ld]\n", type); 
        printf("err_code ==>[%ld]\n", err_code);
        printf("msg      ==>[%s]\n",  msg);        
        strcpy(err_msg, msg);

        if ((transf = (FBFR *)tpalloc("FML", NULL, 0)) == NULL) {
             printf("tpalloc failed! errno = %d\n", tperrno);
        }

        switch(type) {
            case INP:
                switch(err_code) {
                       case -1000:
                            strcpy(err_msg, "해당 사용자에게 권한이 없습니다.");
                            break;
                       default:
                            strcpy(err_msg,
                                   "Input Error Message가 등록되어 있지 않습니다.");
                 }
                break;
            case ORA:
                if (strlen(err_msg)== 0) 
                    strcpy(err_msg, sqlca.sqlerrm.sqlerrmc);
                break; 
            case TMX:
                if (strlen(err_msg)== 0) 
                    strcpy(err_msg, tpstrerror(tperrno));
                break;
            case APP:
                if (strlen(err_msg)== 0) {
                    /* err_mssg=""이면 오류메시지를 setting한다. ******/
                    switch(err_code) {
                           case -500:        /* SYSTEM 관련 오류 */
                                 strcpy(err_msg,"File 생성 오류입니다.");
                                 break;
                           case -502:        
                                 strcpy(err_msg,"내부 서비스를 호출하지 못했습니다.");
                                 break;
                           case -504:
                                 strcpy(err_msg, "소켓 통신 오류입니다.");
                                 break;
                           case -505:  /* 다른 transation에서 수정되었을 경우 MSG처리*/
                                /* "조회 이후에 다른 곳에서 [%s]자료가 변경되었습니다.
                                \n\n다시 조회한 후 처리하십시오.": CLIENT에서 처리*/
                                strcpy(err_msg, "이 없습니다.");
                                break;
                           case -5002: 
                                strcpy(err_msg, "전산실에 문의하십시오.");
                                break;
                           default:
                                strcpy(err_msg,
                               "Application Error Message가 등록되어 있지 않습니다.");
                    }
                }
                break;
        }

        /* ROLLBACK        */
        EXEC SQL WHENEVER SQLERROR CONTINUE;
        EXEC SQL ROLLBACK;

        fbchg_tu ( transf, E_TYPE, i, (char *)&type,0);
        fbchg_tu ( transf, E_CODE, i, (char *)&err_code,0);
        fbchg_tu ( transf, E_MSG,  i, (char *)err_msg,0);
        fbchg_tu ( transf, E_TMP,  i, (char *)&tmp,0);

        tpreturn(TPFAIL, 0, (char *)transf, 0L, 0L);
}

Makefile

다음은 emp_c.pc 소스를 Tmax 애플리케이션으로 만드는 Makefile의 예제이다.

<emp_c.mk>

include $(ORACLE_HOME)/precomp/lib/env32.mk
ORALIBDIR = $(LIBHOME)
ORALIB = -L/home/oracle/OraHome/lib32/ -lclntsh  -lld -lm 
         `cat /home/oracle/OraHome/lib32/sysliblist`  -lm  -lc_r -lpthreads
TARGET  = emp_c
APOBJS  = emp_c.o 
NSDLOBJ = $(TMAXDIR)/lib/sdl.o
#CC
CC=cc

#Oracle
LIBS = -lsvr -loras

OBJS = $(APOBJS) $(SVCTOBJ) 
SVCTOBJ = $(TARGET)_svctab.o 

CFLAGS  = -q32 -O -I$(TMAXDIR)
LDFLAGS = -brtl
APPDIR  = $(TMAXDIR)/appbin
SVCTDIR = $(TMAXDIR)/svct
TMAXLIBDIR  = $(TMAXDIR)/lib
 #
.SUFFIXES : .c

.c.o: 
        $(CC) $(CFLAGS) $(LDFLAGS) -c $<

all: $(TARGET) 

$(TARGET): $(OBJS)
           $(CC) $(CFLAGS) $(LDFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) 
           -L$(ORALIBDIR) $(ORALIB) $(OBJS) $(LIBS) $(NSDLOBJ)
           mv $(TARGET) $(TMAXDIR)/appbin

$(APOBJS): $(TARGET).pc
           proc iname=emp_c include=$(TMAXDIR) define=__LINUX_ORACLE_PROC__
           $(CC) $(CFLAGS) $(LDFLAGS) -c $(TARGET).c

$(SVCTOBJ):
           touch $(SVCTDIR)/$(TARGET)_svctab.c
           $(CC) $(CFLAGS) $(LDFLAGS) -c $(SVCTDIR)/$(TARGET)_svctab.c
#
clean:
          -rm -f *.o core $(TARGET) $(TARGET).lis