view src/reditor.c @ 29:ef6d2a18d7c2

*** empty log message ***
author kono
date Tue, 28 Oct 2008 01:34:41 +0900
parents 24808249e776
children 8d4ffb7c9f4e
line wrap: on
line source

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>

#include "vim.h"
#include "reditor.h"

/* Global variables of Vim */
#include "globals.h"

/* Wrapper for Vim */
static void e_msg_wrp(char *msg);
static void free_wrp(void *p);
static BUFTYPE* make_new_buf_wrp(char *name);
static void free_buf_wrp(BUFTYPE *buf);
static BUFTYPE* find_buf_by_name_wrp(char *name);
static void open_buffer_memline_wrp(BUFTYPE *buf);
static BUFTYPE* get_curbuf_wrp();
static BUFTYPE* set_curbuf_wrp(BUFTYPE *buf);
static char* get_memline_wrp(BUFTYPE *buf, long lnum);
static int append_memline_wrp(long lnum, char *text);
static int delete_memline_wrp(long lnum);
static void update_screen_now_wrp();
static char *get_fullpath_wrp(BUFTYPE *buf);
/* Wrapper END */


static rep_T* get_rep();

static int rep_connect(char *host);

static void rep_free(void *obj);

static BUFTYPE* get_buf_by_name(char *name);
static Session* init_session(Session *sn);
static Session* make_session(char *name, BUFTYPE *buf);
static void free_session(Session *sn);
static void free_session_list(Session *head);
static Session* set_cursession(Session *sn);
static Session* find_session_by_buf(BUFTYPE *buf);
// static Session* find_session_by_name(char *name);
static char* get_fullpath(Session *session);
static Session * get_waiting_session(int sid, char *text) ;
static void set_waiting_session(Session *session);

static int writen(int fd, char *textbuf, unsigned int len);

static rep_cmd* make_cmd(unsigned int cmd,
                         unsigned int sid,
                         unsigned int eid,
                         unsigned int seq,
                         unsigned int lnum,
                         unsigned int length,
                         char *text);
static void forwardCommand(rep_T *rep, rep_cmd *command);
static void set_cmd_seq( rep_cmd *command, unsigned int cmd, unsigned int seq);

static int free_cmd(rep_cmd *cmd);
static int free_cmdlist(rep_cmdlist *cmdlist);
static void add_cmd_to_list(rep_cmdlist *cmdlist, rep_cmd *cmd);

static unsigned int get_header(char *buf,int offset);
static int set_header(unsigned int num, char *pkt, int offset);

static int rep_exe_cmd(rep_cmd *command);
static int rep_exe_pktlist(rep_cmdlist *cmdlist);

static int rep_recv_cmds(int fd, rep_cmdlist *txtcmdlist);
static int rep_send_cmds(int fd, rep_cmdlist *cmdlist);
static int rep_send_cmd( int fd, rep_cmd *cur);


/* g_rep has an all information of Remote Editor */
static rep_T g_rep;

int lock_editor = 0;

/*
 * Wrapper for vim
 */

/* エラーメッセージ出力 */
static void
e_msg_wrp(msg)
    char * msg;
{
    EMSG(msg);
}

/* 通常のメッセージを出力 */
void
puts_msg_wrp(msg)
    char * msg;
{
    MSG_PUTS(msg);
}

static void
free_wrp(p)
    void *p;
{
    vim_free(p);
    return;
}


/* 空の新しいバッファを取得 */
static BUFTYPE *
make_new_buf_wrp(name)
    char * name;
{
    return buflist_new((char_u*)name, NULL, 0, BLN_LISTED);
}

static void
free_buf_wrp(buf)
    BUFTYPE *buf;
{
    close_buffer(NULL, buf, DOBUF_DEL);
    return;
}


/* 名前からバッファへのポインタを取得 */
static BUFTYPE *
find_buf_by_name_wrp(name)
    char * name;
{
    char *sfname = NULL; // sfname is used name's address
    
    BUFTYPE *buf = NULL;  //???

    fname_expand(buf, (char_u**)&name, (char_u**)&sfname);
    buf = buflist_findname((char_u*)name);

    free_wrp(name);
    return buf;
}

static void
open_buffer_memline_wrp(buf)
    BUFTYPE *buf;
{
    BUFTYPE *oldbuf;
    oldbuf = curbuf;
    curbuf = buf;
    open_buffer(FALSE, NULL);
    curbuf = oldbuf;
    return;
}


/* 現在編集中のバッファへのポインタを取得 */
extern BUFTYPE *curbuf;
static BUFTYPE *
get_curbuf_wrp()
{
    return curbuf;
}

/* buf を編集対象にする。
 *それまで編集対象だったバッファへのポインタを返す */
static BUFTYPE *
set_curbuf_wrp(buf)
    BUFTYPE *buf;
{
    BUFTYPE *cb;
    if (buf == NULL)
        return NULL;

    cb = get_curbuf_wrp();
    set_curbuf(buf,DOBUF_GOTO);

    return cb;
}

/* 指定した行番号の行のテキスト(文字列)のポインタを取得 */
static char *
get_memline_wrp(buf, lnum)
    BUFTYPE *buf;  // buf is curbuf
    long lnum;
{
    return (char*)ml_get_buf(buf, lnum, FALSE);
}

/* 編集中のバッファの行の挿入 */
static int
append_memline_wrp(lnum, text)
    long lnum;
    char *text;
{
    int r;
    int permit;
    rep_T *rep;
    rep = get_rep();
    permit = rep->permit;
    rep->permit = FALSE;
    
    r = ml_append(lnum, (char_u*)text, strlen(text)+1, FALSE);
    appended_lines_mark(lnum,1);

    rep->permit = permit;
    return r;
}

/* 編集中のバッファの行数を返す */
long
get_bufmaxline_wrp(buf)
    BUFTYPE *buf;
{
    return buf->b_ml.ml_line_count;
}

/* 編集中のバッファの行の削除 */
static int
delete_memline_wrp(lnum)
    long lnum;
{
    int r;
    int permit;
    rep_T *rep;
    rep = get_rep();
    permit = rep->permit;
    rep->permit = FALSE;

    int maxline = get_bufmaxline_wrp(get_curbuf_wrp());
    if (lnum+1>maxline) return 0;
    r = ml_delete(lnum+1, FALSE);
    deleted_lines_mark(lnum+1,1);

    rep->permit = permit;
    return r;
}

/* バッファの編集後の後処理 */
static void 
update_screen_now_wrp()
{
    check_cursor();
    update_screen(CLEAR);
    return;
}


/* get full path of buffer */
static char *
get_fullpath_wrp(buf)
    BUFTYPE *buf;
{
    return (char*)buf->b_ffname;
}


/* Wrapper END */


static void
puts_sys_err()
{
    char *err = strerror(errno);
    char *errmsg = (char*)alloca(strlen(err)+50);

    sprintf(errmsg, "rep>> %d:%s", errno, err);
    e_msg_wrp(errmsg);
    return;
}


static rep_T*
get_rep()
{
    return(&g_rep);
}

int
rep_permit()
{
    return(g_rep.permit);
}

int
rep_session_permit()
{
    return(g_rep.permit && g_rep.cursession && g_rep.cursession->permit);
}

void
rep_start_create_cmds()
{
    if (g_rep.cursession) {
        g_rep.cursession->permit = TRUE;
    }
    return;
}

void
rep_stop_create_cmds()
{
    if (g_rep.cursession) {
        g_rep.cursession->permit = FALSE;
    }
    return;
}


int
rep_init()
{
    /*
     * g_rep is global variable and it is zero cleared.
     */

    char def_hostname[] = "localhost";
    
    // 現在編集対象のバッファはセッションに加える?
    g_rep.shead = NULL; 

    g_rep.cursession = NULL;
    g_rep.servername = NULL;
    g_rep.waiting_session = NULL;
    
    g_rep.smfd = -1;

    g_rep.eid = 0;
    g_rep.seqno = 0;
    g_rep.prevSeq = 0;
    g_rep.syncMode = 0;
    
    g_rep.permit = FALSE;
   
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif
    g_rep.hostname = (char *)malloc(HOST_NAME_MAX);
    if (gethostname(g_rep.hostname, sizeof(g_rep.hostname)) < 0) {
        strncpy(g_rep.hostname, def_hostname, sizeof(def_hostname)+1);
    }
    g_rep.nop = make_cmd(REPCMD_NOP, 0, 0, 0, 0, 0, "");
    
    return TRUE;
}


static char default_host[] = "localhost";

static int
rep_connect(host)
    char *host; /* "hostname:portnumber" */
{
    int sock;
    struct hostent        *hp;
    struct sockaddr_in    sin;
    char *tmp;
    int port;
   
    if (host == NULL || *host == '\n') {
	host = default_host;
	port = REP_PORT;
    } else {
	if ((tmp = strchr(host, ':')) == NULL ) {
	    return(-1);
	}
	*tmp = '\0';
	tmp++;
	port = atol(tmp);
    }
    
    if ((hp = gethostbyname(host)) == NULL) {
        e_msg_wrp("rep_open: gethostbyname: ERROR");
        return(-1);
    }
    
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        e_msg_wrp("rep_open: socket : ERROR");
        return(-1);
    }
    
    bzero(&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);

    if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
        e_msg_wrp("rep_open: connect: ERROR");
        return(-1);
    }
    
    return(sock);
}


static BUFTYPE *
get_buf_by_name(name)
    char *name;
{
    BUFTYPE *buf;
    if ((buf = find_buf_by_name_wrp(name)) == NULL) {
        buf = make_new_buf_wrp(name);
    }

    if (buf && (buf->b_ml.ml_mfp == NULL)) {
        open_buffer_memline_wrp(buf);
    }
    
    return buf;
}


/* About Session */

static Session *
init_session(sn)
    Session *sn;
{
    sn->next = NULL;
    sn->buf = NULL;
    sn->sname = NULL;
    sn->new_cmdlist.head = NULL;
    sn->new_cmdlist.num = 0;
    sn->sent_cmdlist.head = NULL;
    sn->sent_cmdlist.num = 0;
    sn->sid = 0;
    sn->permit = FALSE;
    sn->prevline = -1;
    sn->del_cmd = 0;

    return sn;
}

/*
 * Make a new session.
 * if buf is NULL, buffer is found or made new one.
 */
static Session *
make_session(name, buf)
    char *name;
    BUFTYPE *buf;
{
    Session *s;

    char *sname;
    int namelen = strlen(name)+1;
    
    s = (Session*)malloc(sizeof(Session));
    if (s == NULL) {
        return(NULL);
    }
    
    if ((sname = (char*)malloc(namelen)) == NULL) {
        return NULL;
    }
    strncpy(sname,name,namelen);

    init_session(s);

    if (buf == NULL) buf = get_buf_by_name(sname);
    s->buf = buf;

    s->sname = sname;
    
    return s;
}

static void 
free_session(sn)
    Session *sn;
{
    
    free_cmdlist(&sn->new_cmdlist);
    free_cmdlist(&sn->sent_cmdlist);
    free_buf_wrp(sn->buf);
    
    if (sn->sname) rep_free(sn->sname);

    init_session(sn);
    
    rep_free(sn);
    return;
}

static void
free_session_list(head)
    Session *head;
{
    Session *next;
    for (; head; head=next) {
        next = head->next;
        head->next = NULL;
        free_session(head);
    }
    return;
}

static Session *
get_waiting_session(int sid, char *text) {
	rep_T *rep = get_rep();
	Session *session = rep->waiting_session;
	if (session==NULL) return session;
	rep->waiting_session = session->next;
	session->sid = sid;
	if (text!=0) {
	    // set session name to the buffer
	    update_screen_now_wrp();
	}
	return session;
}

static Session*
set_cursession(sn)
    Session *sn;
{
    BUFTYPE *oldbuf = NULL;
    Session *oldsn;
    rep_T *rep = get_rep();

    if (sn) oldbuf = set_curbuf_wrp(sn->buf);
    rep->cursession = sn;

    oldsn = find_session_by_buf(oldbuf);
    return oldsn;
}

static Session*
find_session_by_id(id)
    unsigned int id;
{
    Session *cursn = NULL;
    rep_T *rep = get_rep();

    for (cursn = rep->shead; cursn; cursn = cursn->next) {
        if (cursn->sid == id) {
            return cursn;
        }
    }

    return NULL;
}

static Session*
find_session_by_buf(buf)
    BUFTYPE *buf;
{
    Session *cursn = NULL;
    rep_T *rep  = get_rep();

    if (buf == NULL) return NULL;
    
    for (cursn = rep->shead; cursn; cursn = cursn->next) {
        if (cursn->buf == buf) break;
    }
    return cursn;
}

/*
static Session*
find_session_by_name(name)
    char *name;
{
    BUFTYPE *buf;
    buf = find_buf_by_name_wrp(name);
    return find_session_by_buf(buf);
}
 */


static char*
get_fullpath(session)
    Session *session;
{
    BUFTYPE *buf;
    if (session == NULL) {
        buf = get_curbuf_wrp();
    } else {
        buf = session->buf;
    }
    return get_fullpath_wrp(buf);
}

static Session * 
append_session(Session *s1,Session *s2)
{
    Session *s3=s1;
    if (s1==NULL) return s2;
    while(s1->next) s1 = s1->next;
    s1->next = s2;
    return s3;
}

static void 
set_waiting_session(Session *session)
{
    rep_T *rep = get_rep();
    rep->waiting_session = append_session(rep->waiting_session,session);
}

/* End Session */

static int
rep_join_put(int cmd)
{
    int sock;
    rep_T *rep;
    
    rep = get_rep();
    if ((sock = rep_connect(NULL)) < 0) {
        return FALSE;
    }
    if (rep->smfd > 0) {
        close(rep->smfd);
    }

    rep->smfd = sock;
    rep->permit = TRUE;

    /* get current buffer name */
    char *sname;
    if ((sname = get_fullpath(rep->cursession)) == NULL) {
	sname =NO_NAME;  /* the buffer has not name */
    }
    BUFTYPE *buf = get_curbuf_wrp();
    Session *session = make_session(sname,buf);
    set_waiting_session(session);

    
    rep_cmd *command = make_cmd(cmd, 0, rep->eid, rep->seqno++, 0, 0, "");
    forwardCommand(rep, command);
    free_cmd(command);

    return TRUE;

}

int rep_join()
{
    return rep_join_put(SMCMD_JOIN);
}

int
rep_put()
{
    return rep_join_put(SMCMD_PUT);
}

int
rep_remove()
{
    rep_T *rep = get_rep();
    rep_cmd *cmd;
    Session *sn = rep->cursession;

    if (rep->smfd < 0) {     /* session does not exist */
        EMSG("Session does not exist.");
        return FALSE;
    }
    
    cmd = make_cmd(REPCMD_CLOSE, sn->sid, rep->eid, rep->seqno, 0, 0, "");
    forwardCommand(rep,cmd);
    free_cmd(cmd);

    return TRUE;
}



/* Session End */

static void
rep_free(obj)
    void *obj;
{
    if (obj) vim_free(obj);
    return;
}


static int
writen(sock, pkt, len)
    int sock;
    char *pkt;
    unsigned int len;
{
    int offset;
    int written;

    if (len == 0) return 0;

    for (offset=0, written=0; offset<len; offset += written) {
        if ((written = write(sock, pkt + offset, len - offset)) < 0) {
            puts_sys_err();
            return written;
        }
    }
    return offset;
}

static int
readn(sock, pkt, len)
    int sock;
    char *pkt;
    unsigned int len;
{
    unsigned int r;
    unsigned int offset;

    if (len == 0) return 0;
    
    for (offset=0, r=0; offset<len; offset += r) {
        if ((r = read(sock, pkt + offset, len - offset)) < 0) {
            puts_sys_err();
            return r;
        } else if (r==0) return 0;
    }
    return offset;
}


static void
make_packet(command, cmd, sid, eid, seq, lnum, len, text)
    rep_cmd *command;
    unsigned int cmd;
    unsigned int sid;
    unsigned int eid;
    unsigned int seq;
    unsigned int lnum;
    unsigned int len;
    char * text;
{
    char *packet = &command->pkt[0];
    set_header(cmd, packet, REP_CMD_OFFSET);
    set_header(sid, packet, REP_SID_OFFSET);
    set_header(eid, packet, REP_EID_OFFSET);
    set_header(seq, packet, REP_SEQNUM_OFFSET);
    set_header(lnum, packet, REP_LNUM_OFFSET);
    set_header(len, packet, REP_T_SIZE_OFFSET);

    if (text && len>0) {
        memcpy(packet+REP_TEXT_OFFSET, text, len);
    }
    return;
}


static rep_cmd*
make_cmd(cmd, sid, eid, seq, lnum, length, text)
    unsigned int cmd;
    unsigned int sid;
    unsigned int eid;
    unsigned int seq;
    unsigned int lnum;
    unsigned int length;
    char *text;
{
    rep_cmd *cmd_p;
    cmd_p = (rep_cmd*)malloc(sizeof(rep_cmd)+REP_HEADER_SIZE+length);
    if (cmd_p == NULL) return(NULL);
    cmd_p->next = NULL;
    cmd_p->cmd  = cmd;
    cmd_p->sid  = sid;
    cmd_p->eid  = eid;
    cmd_p->seq  = seq;
    cmd_p->len  = length;
    cmd_p->lnum = lnum;
    make_packet(cmd_p, cmd, sid, eid, seq, lnum, length, text);
    return(cmd_p);
}

static void
set_cmd_seq(cmd_p,cmd,seq)
     rep_cmd *cmd_p;
     unsigned int cmd;
     unsigned int seq;
{
    char *packet = &cmd_p->pkt[0];
    cmd_p->seq  = seq;
    cmd_p->cmd  = cmd;
    set_header(cmd, packet, REP_CMD_OFFSET);
    set_header(seq, packet, REP_SEQNUM_OFFSET);
}

static int
free_cmd(cmd)
    rep_cmd *cmd;
{
    if (cmd == NULL) return(FALSE);
    rep_free(cmd);
    return(TRUE);
}

static int
free_cmdlist(cmdlist)
    rep_cmdlist *cmdlist;
{
    rep_cmd *cur;
    rep_cmd *next;
    
    if (cmdlist->head==NULL) return(FALSE);
    for (cur=cmdlist->head; cur; cur=next) {
        next=cur->next;
        free_cmd(cur);
    }
    cmdlist->head = NULL;
    cmdlist->num = 0;
    return(TRUE);
}

static void
add_cmd_to_list(cmdlist, cmd)
    rep_cmdlist *cmdlist;
    rep_cmd *cmd;
{
    rep_cmd *p;
    int count = 0;

    for (p=cmd; p; p=p->next) count++;

    if (cmdlist->head) {
        for (p = cmdlist->head; p->next; p = p->next);
        p->next = cmd;
    } else {
        cmdlist->head = cmd;
    }
    cmdlist->num += count;
    return;
}


/*
    Before any line changes, keep the lines as DELETE_LINE_CMD.

    xtr==0    ml_replace
    xtr==1    ml_append
    xtr==-1   ml_delete
 */
void
rep_prevline_flush(int lnum, int xtr)
{
    BUFTYPE *cbuf;
    Session *cursn;
    rep_cmd *cmd;
    rep_T *rep = get_rep();

    cursn = rep->cursession;

    if (cursn == NULL)
	return;

    // バッファが変更された場合には rep->cursession も合わす
    if ((cbuf = get_curbuf_wrp()) != cursn->buf) {
        cursn = find_session_by_buf(cbuf);
        if (cursn == NULL)
            return;
        rep->cursession = cursn;
    }

    if(cursn->prevline!=lnum&&xtr!=0) {
	if (cursn->del_cmd) {
	    // Cancel delete cmd 
	    rep_free(cursn->del_cmd);
	    cursn->del_cmd = 0;
        }
    }
    cursn->prevline=lnum;
    if (xtr<0) {
	// ml_delete case
	char *text = get_memline_wrp(cursn->buf, lnum);
	unsigned int length = strlen(text);
	cmd = make_cmd(REPCMD_DELETE, cursn->sid, rep->eid, rep->seqno++, 
	    cursn->prevline-1, length, text);
	rep_send_cmd(rep->smfd,cmd);
	rep_free(cmd);
	if (cursn->del_cmd) {
	    // Cancel delete cmd 
	    rep_free(cursn->del_cmd);
	    cursn->del_cmd = 0;
        }
    } else if (xtr==0) {
	// ml_replace case
	if (cursn->del_cmd) 
	    return;       // already line saved do nothing
	char *text = get_memline_wrp(cursn->buf, lnum);
	unsigned int length = strlen(text);
	cursn->del_cmd = make_cmd(REPCMD_DELETE, cursn->sid, rep->eid, 
	    rep->seqno++, cursn->prevline-1, length, text);
	
    } else {
	// ml_append case
	// do nothing
    }
}

/*
 *   channged_common から呼ばれて、変更をREP commandに直して
 *   送信する。
 */
void
rep_register(lnum, lnume, xtra)
    unsigned int lnum;
    unsigned int lnume;
    int xtra;
{
    int i;
    BUFTYPE *cbuf;
    Session *cursn;
    rep_cmd *cmd;
    rep_T *rep = get_rep();
    
    if ((cursn = rep->cursession) == NULL) return ;

    // バッファが変更された場合には rep->cursession も合わす
    if ((cbuf = get_curbuf_wrp()) != cursn->buf) {
        cursn = find_session_by_buf(cbuf);
        if (cursn == NULL)
            return ;
        rep->cursession = cursn;
    }
    if (xtra<0) {
	// delete case, the command was sent, do nothing
	cursn->prevline=-1;
    } else if (xtra>0) {
	// append case
    
	for(i=lnum;i<lnum+xtra;i++) {
	    char *text = get_memline_wrp(cursn->buf, i);
	    unsigned int length = strlen(text);
	    // make INSERT_CMD for insert or changed lines if any
	    cmd = make_cmd(REPCMD_INSERT, cursn->sid, rep->eid, rep->seqno++, 
		i-1, length, text);
	    rep_send_cmd(rep->smfd,cmd);
	    free_cmd(cmd);
	}
	cursn->prevline=-1;
    } else if (xtra==0) {
	// replace case

	// send saved DELETE command
	if (cursn->del_cmd!=0) {
	    cmd = cursn->del_cmd;
	    rep_send_cmd(rep->smfd,cmd);
	    rep_free(cmd); cmd=0;
	    cursn->del_cmd = 0;
	} else {
	    // first insert case?
	}
	cursn->prevline = lnum;

	// send saved new line as INSERT command
	char *text = get_memline_wrp(cursn->buf, lnum);
	unsigned int length = strlen(text);
	cmd = make_cmd(REPCMD_INSERT, cursn->sid, rep->eid, rep->seqno++, 
	    cursn->prevline-1, length,text);
	rep_send_cmd(rep->smfd,cmd);
	// reuse cmd
	// save current line for next replace
	set_cmd_seq(cmd,REPCMD_DELETE,rep->seqno++);
	cursn->del_cmd = cmd;
    }

}


static int
set_header(data, pkt, offset)
    unsigned int data;
    char         *pkt;
    int          offset;
{
    int *ipkt;
    int ndata = htonl(data);

    ipkt = (int*)pkt;
    ipkt[offset/4] = ndata;

    return(TRUE);    
}

static unsigned int
get_header(pkt, offset)
	char  *pkt;
	int offset;
{
	int *ipkt;
	int data;
	unsigned int header;

	ipkt   = (int *)pkt;
	data   = ipkt[offset/4];
	header = (unsigned int)ntohl(data);

	return(header);
}

/*
     REP command packet flow

     Own change

     CMD ------>  session manager
                  session manager <--- CMD   command return
         <------  MERGE_START (block user input)
         <------  Merge fix from session manager
     CMD ------>  session manager        return same command as ack
         <------  MERGE_END              command return
                              (enable user input)

                  session manager <--- CMD   incomming external command 
     NOP ------>  session manager        if we have sent any command
     CMD ------>  session manager        return same command as ack

 */

static void
forwardCommand(rep_T *rep, rep_cmd *command)
{
    int fd = rep->smfd;
    writen(fd,command->pkt,command->len+REP_HEADER_SIZE);
}

static void
incrementSeq(rep_T *rep)
{
    rep->seqno++;
}

static void
addNop(int sid, rep_T *rep) 
{
    int fd = rep->smfd;
    char *packet = &rep->nop->pkt[0];

    if (rep->prevSeq==rep->seqno) {
	set_header(sid, packet, REP_SID_OFFSET);
	set_header(rep->eid, packet, REP_EID_OFFSET);
	set_header(rep->seqno,  packet, REP_SEQNUM_OFFSET);
	rep->prevSeq = rep->seqno;
	incrementSeq(rep);
	writen(fd,rep->nop->pkt,rep->nop->len+REP_HEADER_SIZE);
    }
}

static int
rep_exe_cmd(command)
    rep_cmd *command;
{
    char *text = command->pkt+REP_TEXT_OFFSET;
    int eid = command->eid;
    rep_T *rep = get_rep();
    Session *session;
    
    session = find_session_by_id(command->sid);

    switch (command->cmd) {
    case SMCMD_JOIN_ACK:
        {
	session = get_waiting_session(command->sid,text);
        set_cursession(session);
        rep_start_create_cmds();
	rep->eid = command->eid;

	char *msg = alloca(150);
	sprintf(msg,"joined eid=%d sid=%d",rep->eid,session->sid);
	e_msg_wrp(msg);

	rep->prevSeq = rep->seqno;
        }

        break;

    case SMCMD_PUT_ACK:
        {
        /* Enter Session */
	session = get_waiting_session(command->sid,text);
        /* set session to cursession */
        set_cursession(session);
        rep_start_create_cmds();
	rep->eid = command->eid;

	char *msg = alloca(150);
	sprintf(msg,"put eid=%d sid=%d",rep->eid,session->sid);
	e_msg_wrp(msg);

	rep->prevSeq = rep->seqno;
        }

        break;
    case REPCMD_INSERT:
        append_memline_wrp(command->lnum, text);
	if (eid!=MERGE_EID) addNop(command->sid, rep);
	forwardCommand(rep,command);
        if (eid!=MERGE_EID) update_screen_now_wrp();
        break;
    case REPCMD_DELETE:
        delete_memline_wrp(command->lnum);
        if (eid!=MERGE_EID) update_screen_now_wrp();
    case REPCMD_NOP:
	if (eid!=MERGE_EID) addNop(command->sid, rep);
	forwardCommand(rep,command);
        break;
    case SMCMD_SYNC:
	rep->syncMode = 1;
	set_cmd_seq(command,SMCMD_SYNC_ACK,rep->seqno++);
	forwardCommand(rep,command);
        break;
    case SMCMD_QUIT:
	forwardCommand(rep,command);
        break;
    case SMCMD_START_MERGE:
	lock_editor = 1;
	set_cmd_seq(command,SMCMD_START_MERGE_ACK,rep->seqno++);
	forwardCommand(rep,command);
        break;
    case SMCMD_END_MERGE:
	lock_editor = 0;
	rep->prevSeq = rep->seqno;
        update_screen_now_wrp();
    default:
        break;
    }
    free_cmd(command);
    return(TRUE);
}


/* execute command list based cmd packet */
static int
rep_exe_pktlist(cmdlist)
    rep_cmdlist *cmdlist;
{
    rep_cmd *repcmd;
    
    if ((cmdlist == NULL) || (cmdlist->head == NULL)) {
        return(FALSE);
    }

    for (repcmd = cmdlist->head; repcmd; repcmd = repcmd->next) {
	rep_exe_cmd(repcmd);
    }
    
    return(TRUE);
}



static int
rep_recv_cmds(fd, txtcmdlist)
    int fd;
    rep_cmdlist *txtcmdlist;
{
    unsigned int cmd;
    unsigned int sid;
    unsigned int eid;
    unsigned int seq;
    unsigned int lnum;
    unsigned int textsize;
    rep_cmd *cmd_p ; 
    char *text;
    char *header;
    
    if (fd < 0) {
        return(FALSE);
    }
    if ((cmd_p = (rep_cmd *)malloc(sizeof(rep_cmd)+128)) == NULL) 
	return FALSE;

    text =  &cmd_p->pkt[REP_TEXT_OFFSET];
    header =  &cmd_p->pkt[0];
    
    /* read header part */
    if (readn(fd, header, REP_HEADER_SIZE) <= 0) {
        puts_sys_err();
        return(FALSE);
    }

    cmd = get_header(header, REP_CMD_OFFSET);
    sid = get_header(header, REP_SID_OFFSET);
    eid = get_header(header, REP_EID_OFFSET);
    seq = get_header(header, REP_SEQNUM_OFFSET);
    lnum = get_header(header, REP_LNUM_OFFSET);
    textsize = get_header(header, REP_T_SIZE_OFFSET);
    if ((cmd_p = (rep_cmd *)realloc(cmd_p,sizeof(rep_cmd)+textsize)) == NULL) 
	return FALSE;

    if (textsize > 0) {
        /* read text part */
        if (readn(fd, text, textsize) <= 0) {
            puts_sys_err();
            rep_free(cmd_p);
            return(FALSE);
        }
	text[textsize] = 0;
    }
    cmd_p->next = NULL;
    cmd_p->cmd  = cmd;
    cmd_p->sid  = sid;
    cmd_p->eid  = eid;
    cmd_p->seq  = seq;
    cmd_p->len  = textsize;
    cmd_p->lnum = lnum;

    add_cmd_to_list(txtcmdlist, cmd_p);
    return(TRUE);
}

void
rep_send_cur_cmdlist()
{
    rep_T *rep_p = get_rep();
    
    rep_send_cmds(rep_p->smfd,&(rep_p->cursession->new_cmdlist));

    free_cmdlist(&rep_p->cursession->sent_cmdlist);

    rep_p->cursession->sent_cmdlist.head = rep_p->cursession->new_cmdlist.head;
    rep_p->cursession->sent_cmdlist.num = rep_p->cursession->new_cmdlist.num;
    
    rep_p->cursession->new_cmdlist.head = NULL;
    rep_p->cursession->new_cmdlist.num  = 0;
    
    return;
}

static int
rep_send_cmd(fd,cur)
    int fd;
    rep_cmd *cur;
{
    return writen(fd,cur->pkt,cur->len+REP_HEADER_SIZE );
}

static int
rep_send_cmds(fd,cmdlist)
    int fd;
    rep_cmdlist *cmdlist;
{
    rep_cmd *cur;
    
    if ((fd<0) || (cmdlist == NULL)) {
        return(FALSE);
    }

    if ((cmdlist->head == NULL) || (cmdlist->num == 0)) {
        return(TRUE);
    }
    
    for (cur = cmdlist->head; cur; cur = cur->next) {
        if (writen(fd,cur->pkt,cur->len+REP_HEADER_SIZE)==FALSE) {
            return(FALSE);
        }    
    }
    return(TRUE);
}

int
rep_fd_check(fd, rfds_p, efds_p)
    int fd;         // input from keyboard or something...
    fd_set *rfds_p; // readable fds
    fd_set *efds_p; // include a error fd
{
    rep_T *rep_p;
    rep_cmdlist txtcmdlist = {NULL,0};

    rep_p = get_rep();
    if ((rep_p->smfd > 0) && (FD_ISSET(rep_p->smfd, rfds_p))) {
        if (rep_recv_cmds(rep_p->smfd, &(txtcmdlist)) == FALSE) {
            close(rep_p->smfd);
            rep_p->smfd = -1;
        } else {
	    rep_exe_pktlist(&txtcmdlist);
	}
    }

    if (FD_ISSET(fd, rfds_p) || FD_ISSET(fd, efds_p)) {
        return(TRUE);
    }
    return FALSE;
}



int
rep_fd_set(rfds_p, efds_p, max_fds)
    fd_set *rfds_p;
    fd_set *efds_p;
    int max_fds;
{  
    rep_T *rep_p;
    
    rep_p = get_rep();
    
    if (rep_p->smfd > 0) {
        FD_SET(rep_p->smfd,rfds_p);
        FD_SET(rep_p->smfd,efds_p);
        if(max_fds < rep_p->smfd){
            max_fds = rep_p->smfd;
        }
    }

    return(max_fds);
}

void
rep_close(int i)
{
    rep_T *rep_p;
    
    rep_p = get_rep();
    // What should I do?
    rep_end();
    
}

/*
 * read などで待つ場合に、この関数で REP 関連のデータをチェックする
 * 指定した fd ( read で読みこむ) から入力があるとぬける。
 */
int
rep_select(fd)
    int fd;
{
    fd_set rfds_p;
    fd_set efds_p;
    int sk;
    int max_fds = MAX_FDS;

    struct timeval zerotime;
    zerotime.tv_sec = 0;
    zerotime.tv_usec = 0;

    if (fd < 0) return FALSE;

    for(;;) {
	/* select の中で modify されてるので、初期化 */
	// struct timeval tv;
	// tv.tv_sec = 0;
	// tv.tv_usec = 100000;
	FD_ZERO(&rfds_p);
	FD_ZERO(&efds_p);

	FD_SET(fd,&rfds_p);

	max_fds = rep_fd_set(&rfds_p, &efds_p, max_fds);

	if ((sk = select(max_fds+1, &rfds_p, NULL, &efds_p, NULL)) < 0) {
	    if (errno == EBADF){
		int i;
		for(i = 0;i < max_fds;i++){
		    fd_set suspect;
		    FD_ZERO(&suspect);
		    if (FD_ISSET(i, &rfds_p)){
			FD_SET(i, &suspect);
			if (select(max_fds, &suspect, NULL, NULL, &zerotime) == FAIL){
			    FD_CLR(i, &rfds_p);
			    rep_close(i);
			    // we have to something to prevent to write to this
			    // port...
			    return(TRUE);
			}
			FD_CLR(i, &suspect);
		    }
		}
	    } else {
		e_msg_wrp("rep_select(): ERROR");
		return(FALSE);
	    }

	}
	if ( rep_fd_check(fd, &rfds_p, &efds_p)) 
	    return TRUE;  // fd に入力があったので抜ける
    }
}

void
rep_end()
{
    rep_T *rep_p;
    rep_p = get_rep();
    
    if (rep_p->shead) free_session_list(rep_p->shead); // cursession is freed 
    if (rep_p->servername) free_wrp(rep_p->servername);
    if (rep_p->smfd > 0) close(rep_p->smfd);

    set_curbuf_wrp(rep_p->scratch_buf);
    
    rep_init();
}



extern void
pcmd(cmd_p) 
    rep_cmd *cmd_p;
{
    int i;
    fprintf(stderr,"cmd=%04x",cmd_p->cmd );
    fprintf(stderr,"sid=%08x", cmd_p->sid);
    fprintf(stderr,"eid=%08x", cmd_p->eid);
    fprintf(stderr,"seq=%08x", cmd_p->seq);
    fprintf(stderr,"lineno=%08x", cmd_p->lnum);
    fprintf(stderr,"sz=%08x", cmd_p->len);
   
    fprintf(stderr,"\n");
    for(i=0;i<cmd_p->len+REP_HEADER_SIZE;i++) {
	fprintf(stderr,"%02x ", cmd_p->pkt[i]);
    }
    fprintf(stderr,"\n");
}

/* end */