/* * 標準入出力でコマンドを受けたり,変換結果を送るなどの通信を * アプリケーション(おもにEmacs)と行うことにより,アプリケーションに * Anthyによる入力機能を容易かつ安全に追加できる. * * Funded by IPA未踏ソフトウェア創造事業 2002 2/26 * Copyright (C) 2001-2002 UGAWA Tomoharu * Copyright (C) 2002-2004 TABATA Yusuke, */ /* * *マルチコンテキストの扱いを決めかねている * *入出力にstdioを使うかfdを使うか決めかねている */ #include #include #include #include #include #include #include #include #include #include #include "rkconv.h" #include extern void egg_main(void); /* 何回次候補を押すと候補の列挙を一覧モードに切替えるか? */ #define DEFAULT_ENUM_CAND_LIMIT 3 /* キーに対応する定数 */ #define KEY_SHIFT 0x00010000 #define KEY_CTRL 0x00020000 #define KEY_ALT 0x00040000 #define KEY_SPACE ' ' #define KEY_OPAR '(' #define KEY_CPAR ')' #define KEY_ENTER 0x00000100 #define KEY_DELETE 0x00000200 #define KEY_LEFT 0x00000300 #define KEY_RIGHT 0x00000400 #define KEY_ESC 0x00000500 #define KEY_BACKSPACE 0x00000600 #define KEY_UP 0x00000700 #define KEY_DOWN 0x00000800 #define KEY_CTRL_A (KEY_CTRL | 'A') #define KEY_CTRL_E (KEY_CTRL | 'E') #define KEY_CTRL_J (KEY_CTRL | 'J') #define KEY_CTRL_K (KEY_CTRL | 'K') #define KEY_CTRL_H (KEY_CTRL | 'H') #define KEY_CTRL_D (KEY_CTRL | 'D') #define KEY_SHIFT_LEFT (KEY_SHIFT | KEY_LEFT) #define KEY_SHIFT_RIGHT (KEY_SHIFT | KEY_RIGHT) #define BUF_GROW_SIZE 4096 #define MAX(a,b) ((a) > (b) ? (a) : (b)) /* * コマンドにはキーが押されたことを示す普通のコマンドと * 高水準な命令のハイレベルコマンドがある. */ enum { /* ハイレベルコマンド */ CMDH_IGNORE_ICTXT, CMDH_GETPREEDIT, CMDH_SELECT_CONTEXT, CMDH_RELEASE_CONTEXT, CMDH_MAP_EDIT, CMDH_MAP_SELECT, CMDH_GET_CANDIDATE, CMDH_SELECT_CANDIDATE, CMDH_CHANGE_TOGGLE, CMDH_MAP_CLEAR, CMDH_SET_BREAK_INTO_ROMAN, CMDH_SET_PREEDIT_MODE, CMDH_PRINT_CONTEXT, /* キーコマンド */ CMD_SPACE = 1000, CMD_ENTER, CMD_BACKSPACE, CMD_DELETE, CMD_UP, CMD_ESC, CMD_SHIFTARROW, CMD_ARROW, CMD_KEY, CMD_GOBOL, CMD_GOEOL, CMD_CUT }; struct high_level_command_type { const char* name; int cmd; int n_arg; int opt_arg; } high_level_command_type[] = { /* コンテキストの情報を表示する */ {"PRINT_CONTEXT", CMDH_PRINT_CONTEXT, 0, 0}, /* トグルに使うキーを変更する */ {"CHANGE_TOGGLE", CMDH_CHANGE_TOGGLE, 1, 0}, /* コンテキストを選択する */ {"SELECT_CONTEXT", CMDH_SELECT_CONTEXT, 1, 0}, {"RELEASE_CONTEXT", CMDH_RELEASE_CONTEXT, 0, 0}, {"MAP_CLEAR", CMDH_MAP_CLEAR, 1, 0}, {"MAP_EDIT", CMDH_MAP_EDIT, 3, 0}, {"MAP_SELECT", CMDH_MAP_SELECT, 1, 0}, {"GET_CANDIDATE", CMDH_GET_CANDIDATE, 1, 0}, {"SELECT_CANDIDATE", CMDH_SELECT_CANDIDATE, 1, 0}, /* バックスペースでローマ字に戻る */ {"BREAK_INTO_ROMAN", CMDH_SET_BREAK_INTO_ROMAN, 1, 0}, /**/ {"SET_PREEDIT_MODE", CMDH_SET_PREEDIT_MODE, 1, 0}, /**/ {NULL, -1, 0, 0} }; struct command { int cmd; char** arg; int n_arg; struct command* next; }; struct connection { char* rbuf; int n_rbuf; int s_rbuf; int rfd; char* wbuf; int n_wbuf; int s_wbuf; int wfd; }; static void send_error(void); static struct connection* conn; static struct anthy_input_config* config; static struct command* command_queue; static int daemon_sock = -1; static int anonymous; static int egg; static char *personality; int use_utf8; static char * encode_command_arg(char *a) { int i, j, len; char *s; len = strlen(a); s = malloc(len + 1); for(i = 0,j = 0; i < len; i++) { if (a[i] != '\\') { s[j] = a[i]; j++; continue; } /* バックスラッシュ */ i++; switch (a[i]) { case 0: case '\\': s[j] = '\\'; j++; break; case '\"': s[j] = '\"'; j++; break; case 'X': { char buf[5]; unsigned char *p; int num; /* ToBeDone エラーチェック */ strncpy(buf, &a[i+1], 4); i+= 5; sscanf(buf, "%x", (unsigned int *)&num); p = (unsigned char *)buf; p[0] = num & 255; p[1] = num >> 8; j += sprintf(&s[j], "%c%c", buf[1] , buf[0]); } break; } } s[j] = 0; return s; } static int ensure_buffer(char** buf, int* size, int to_size) { if (*size < to_size) { *buf = (char*) realloc(*buf, to_size); if (*buf == NULL) { return -1; } *size = to_size; } return 0; } static void kill_connection(struct connection* conn) { (void) conn; exit(0); } static struct command * make_command0(int no) { struct command* cmd; cmd = (struct command*) malloc(sizeof(struct command)); cmd->cmd = no; cmd->n_arg = 0; cmd->arg = NULL; cmd->next = NULL; return cmd; } static struct command * make_command1(int no, const char* arg1) { struct command* cmd; cmd = (struct command*) malloc(sizeof(struct command)); cmd->cmd = no; cmd->n_arg = 1; cmd->arg = (char**) malloc(sizeof(char*) * 1); cmd->arg[0] = strdup(arg1); cmd->next = NULL; return cmd; } static struct key_name_table { const char* name; int code; int is_modifier; } key_name_table[] = { {"shift", KEY_SHIFT, 1}, {"ctrl", KEY_CTRL, 1}, {"alt", KEY_ALT, 1}, {"space", KEY_SPACE, 0}, {"opar", KEY_OPAR, 0}, {"cpar", KEY_CPAR, 0}, {"enter", KEY_ENTER, 0}, {"esc", KEY_ESC, 0}, {"backspace", KEY_BACKSPACE, 0}, {"delete", KEY_DELETE, 0}, {"left", KEY_LEFT, 0}, {"right", KEY_RIGHT, 0}, {"up", KEY_UP, 0}, {NULL, 0, 0} }; /* * エンコードされたキーの情報を取得する */ static int read_encoded_key(char** buf) { char* p; char* str; int key = 0; /* 閉じ括弧を探す */ for (p = *buf + 1; *p; p++) { if (*p == ')') { break; } } if (*p == '\0') { *buf = p; return '\0'; } str = *buf + 1; *p = '\0'; *buf = p + 1; p = strtok(str, " \t\r"); if (!p) { return '\0'; } do { if (p[1] == '\0') { return key | *p; } else { struct key_name_table* e; for (e = key_name_table; e->name; e++) { if (strcmp(e->name, p) == 0) { key |= e->code; if (e->is_modifier == 0) { return key; } } } } } while((p = strtok(NULL, " \t\r"))); return '\0'; } static struct high_level_command_type * find_command_type(char *str) { struct high_level_command_type* cmdn; for (cmdn = high_level_command_type; cmdn->name; cmdn++) { if (!strcmp(str, cmdn->name)) { return cmdn; } } return NULL; } /* ハイレベルコマンドをパースする */ static struct command * make_hl_command(char *buf) { /* high-level command */ struct high_level_command_type* cmdn; struct command* cmd = NULL; char* p; int i; /* コマンドの種類を調べる */ p = strtok(buf, " \t\r"); if (!p) { return NULL; } cmdn = find_command_type(p); if (!cmdn) { return NULL; } /* コマンドを作る */ cmd = (struct command*) malloc(sizeof(struct command)); cmd->cmd = cmdn->cmd; cmd->n_arg = cmdn->n_arg; if (cmd->n_arg > 0) { cmd->arg = (char**) malloc(sizeof(char*) * cmd->n_arg); } else { cmd->arg = NULL; } for (i = 0; i < cmd->n_arg; i++) { p = strtok(NULL, " \t\r"); if (!p) { while (i-- > 0) free(cmd->arg[i]); free(cmd->arg); free(cmd); return NULL; } cmd->arg[i] = encode_command_arg(p); } while ((p = strtok(NULL, " \t\r"))) { if (!p) { break; } cmd->n_arg++; cmd->arg = (char**) realloc(cmd->arg, sizeof(char*) * cmd->n_arg); cmd->arg[cmd->n_arg - 1] = encode_command_arg(p); } cmd->next = NULL; return cmd; } /* 普通のコマンドをパースする */ static struct command * make_ll_command(char *buf) { struct command* cmd_head = NULL; struct command* cmd = NULL; char* p; for (p = buf; *p; ) { struct command* cmd0 = NULL; int c; if (isspace((int)(unsigned char) *p)) { p++; continue; } else if (*p == '(') { c = read_encoded_key(&p); } else { c = *p++; } switch (c) { case '\0': break; case KEY_SPACE: cmd0 = make_command0(CMD_SPACE); break; case KEY_CTRL_J: case KEY_ENTER: case KEY_DOWN: cmd0 = make_command0(CMD_ENTER); break; case KEY_BACKSPACE: case KEY_CTRL_H: cmd0 = make_command0(CMD_BACKSPACE); break; case KEY_DELETE: case KEY_CTRL_D: cmd0 = make_command0(CMD_DELETE); break; case KEY_SHIFT_LEFT: cmd0 = make_command1(CMD_SHIFTARROW, "-1"); break; case KEY_SHIFT_RIGHT: cmd0 = make_command1(CMD_SHIFTARROW, "1"); break; case KEY_LEFT: cmd0 = make_command1(CMD_ARROW, "-1"); break; case KEY_RIGHT: cmd0 = make_command1(CMD_ARROW, "1"); break; case KEY_UP: cmd0 = make_command0(CMD_UP); break; case KEY_ESC: cmd0 = make_command0(CMD_ESC); break; case KEY_CTRL_A: cmd0 = make_command0(CMD_GOBOL); break; case KEY_CTRL_E: cmd0 = make_command0(CMD_GOEOL); break; case KEY_CTRL_K: cmd0 = make_command0(CMD_CUT); break; default: if ((c & 0xffffff80) == 0) { /* ASCII文字 */ char str[2]; str[0] = (char)c; str[1] = '\0'; /* cmd_key */ cmd0 = make_command1(CMD_KEY, str); } break; } if (cmd0) { if (cmd) { cmd->next = cmd0; } else { cmd_head = cmd0; } cmd = cmd0; } } /* for (p) */ if (cmd) { cmd->next = make_command0(CMDH_GETPREEDIT); } else { cmd_head = make_command0(CMDH_GETPREEDIT); } return cmd_head; } static struct command* make_command(char* buf) { if (*buf == ' ') { /* ハイレベルコマンド */ struct command *cmd; cmd = make_hl_command(buf); if (!cmd) { send_error(); } return cmd; } return make_ll_command(buf); } static int proc_connection(void) { fd_set rfds; fd_set wfds; int max_fd; int ret; max_fd = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); if (daemon_sock >= 0) { max_fd = daemon_sock; FD_SET(daemon_sock, &rfds); } max_fd = MAX(conn->rfd, max_fd); FD_SET(conn->rfd, &rfds); if (conn->n_wbuf > 0) { max_fd = MAX(conn->wfd, max_fd); FD_SET(conn->wfd, &wfds); } if (max_fd == -1) return -1; ret = select(max_fd + 1, &rfds, &wfds, NULL, NULL); if (ret < 0) { return -1; } if (conn->n_wbuf > 0 && FD_ISSET(conn->wfd, &wfds)) { ret = write(conn->wfd, conn->wbuf, conn->n_wbuf); if (ret <= 0) { kill_connection (conn); } else { conn->n_wbuf -= ret; if (conn->n_wbuf > 0) { memmove(conn->wbuf, conn->wbuf + ret, conn->n_wbuf); } } } if (FD_ISSET(conn->rfd, &rfds)) { ensure_buffer(&conn->rbuf, &conn->s_rbuf, conn->n_rbuf + BUF_GROW_SIZE); ret = read(conn->rfd, conn->rbuf + conn->n_rbuf, conn->s_rbuf - conn->n_rbuf); if (ret <= 0) { kill_connection (conn); } else { conn->n_rbuf += ret; } } return 0; } static struct command * read_command(void) { struct command* cmd; AGAIN: if (command_queue != NULL) { cmd = command_queue; command_queue = cmd->next; return cmd; } while (1) { char* p; for (p = conn->rbuf; p < conn->rbuf + conn->n_rbuf; p++) { if (*p == '\n') { *p = '\0'; cmd = make_command(conn->rbuf); conn->n_rbuf -= p + 1 - conn->rbuf; memmove(conn->rbuf, p + 1, conn->n_rbuf); if (cmd) { command_queue = cmd; goto AGAIN; } } } if (proc_connection() == -1) { return NULL; } } } static void write_reply(const char* buf) { printf("%s", buf); } static void send_error(void) { write_reply("ERR\r\n"); } static void send_ok(void) { write_reply("OK\r\n"); } static void send_number10(int num) { char buf[20]; sprintf(buf, "%d", num); write_reply(buf); } static void send_string(const char* str) { write_reply(str); } static void send_quote_string(const char* str) { char buf[20]; /* このぐらいあれば大抵大丈夫 */ const char *p; char *q, *end; end = buf + sizeof(buf) - 2; for (q = buf, p = str; *p;) { if (q >= end) { *q = '\0'; write_reply(buf); q = buf; } switch (*p) { case '\"': case '\\': *q++ = '\\'; break; default: break; } *q++ = *p++; } *q = '\0'; write_reply(buf); } static void send_preedit(struct anthy_input_preedit* pedit) { send_string("("); send_number10(pedit->state); if (pedit->commit != NULL) { send_string(" ((COMMIT) \""); send_quote_string(pedit->commit); send_string("\")"); } if (pedit->cut_buf != NULL) { send_string(" ((CUTBUF) \""); send_quote_string(pedit->cut_buf); send_string("\")"); } switch (pedit->state) { case ANTHY_INPUT_ST_OFF: case ANTHY_INPUT_ST_NONE: break; case ANTHY_INPUT_ST_EDIT: case ANTHY_INPUT_ST_CONV: case ANTHY_INPUT_ST_CSEG: { struct anthy_input_segment* seg; for (seg = pedit->segment; seg; seg = seg->next) { if (seg->str == NULL) { if (seg->flag & ANTHY_INPUT_SF_CURSOR) send_string(" cursor"); } else { if (seg->flag & ANTHY_INPUT_SF_CURSOR) { if (seg->flag & ANTHY_INPUT_SF_ENUM) send_string(" ((UL RV ENUM) \""); else if (seg->flag & ANTHY_INPUT_SF_ENUM_REVERSE) send_string(" ((UL RV ENUMR) \""); else send_string(" ((UL RV) \""); } else send_string(" ((UL) \""); send_quote_string(seg->str); send_string("\" "); send_number10(seg->cand_no); send_string(" "); send_number10(seg->nr_cand); send_string(")"); } } } break; } send_string(")\r\n"); } static void send_single_candidate(struct anthy_input_segment* seg) { send_string("(\""); send_quote_string(seg->str); send_string("\" "); send_number10(seg->cand_no); send_string(" "); send_number10(seg->nr_cand); send_string(")\r\n"); } static void free_command(struct command* cmd) { int i; for (i = 0; i < cmd->n_arg; i++) free(cmd->arg[i]); free(cmd->arg); free(cmd); } struct input_context_list { int id; struct anthy_input_context* ictx; struct input_context_list* next; }; static struct input_context_list* ictx_list = NULL; static void new_input_context(int id) { struct input_context_list* ictxl; ictxl = (struct input_context_list*) malloc(sizeof (struct input_context_list)); ictxl->id = id; ictxl->ictx = anthy_input_create_context(config); ictxl->next = ictx_list; ictx_list = ictxl; } static struct anthy_input_context* get_current_input_context(void) { if (ictx_list == NULL) new_input_context(0); return ictx_list->ictx; } static void cmdh_get_preedit(struct anthy_input_context* ictx) { struct anthy_input_preedit* pedit; pedit = anthy_input_get_preedit(ictx); send_preedit(pedit); anthy_input_free_preedit(pedit); } static void cmdh_select_input_context(struct command* cmd) { int id; struct input_context_list** p; id = atoi(cmd->arg[0]); for (p = &ictx_list; *p; p = &(*p)->next) { if ((*p)->id == id) { struct input_context_list* sel; sel = *p; *p = sel->next; sel->next = ictx_list; ictx_list = sel; send_ok(); return; } } new_input_context(id); send_ok(); } static void cmdh_release_input_context(struct command* cmd) { struct input_context_list* sel; (void)cmd; if (!ictx_list) { send_ok(); return ; } sel = ictx_list; ictx_list = ictx_list->next; anthy_input_free_context(sel->ictx); free(sel); send_ok(); } static void cmdh_change_toggle(struct command *cmd) { int toggle = cmd->arg[0][0]; int ret; ret = anthy_input_edit_toggle_config(config, toggle); if (ret != 0) { send_error(); return; } anthy_input_change_config(config); send_ok(); } static void cmdh_map_clear(struct command *cmd) { anthy_input_clear_rk_config(config, atoi(cmd->arg[0])); anthy_input_change_config(config); send_ok(); } static void cmdh_set_break_into_roman(struct command *cmd) { anthy_input_break_into_roman_config(config, atoi(cmd->arg[0])); anthy_input_change_config(config); send_ok(); } static void cmdh_set_preedit_mode(struct command *cmd) { anthy_input_preedit_mode_config(config, atoi(cmd->arg[0])); anthy_input_change_config(config); send_ok(); } static void cmdh_map_edit(struct command* cmd) { /* MAP,from,to */ int map_no = atoi(cmd->arg[0]); int ret; ret = anthy_input_edit_rk_config(config, map_no, cmd->arg[1], cmd->arg[2], NULL); if (ret != 0) { send_error(); return; } anthy_input_change_config(config); send_ok(); } static void cmdh_map_select(struct anthy_input_context* ictx, struct command* cmd) { char* map_name; int map_no; map_name = cmd->arg[0]; if (strcmp(map_name, "alphabet") == 0) map_no = ANTHY_INPUT_MAP_ALPHABET; else if (strcmp(map_name, "hiragana") == 0) map_no = ANTHY_INPUT_MAP_HIRAGANA; else if (strcmp(map_name, "katakana") == 0) map_no = ANTHY_INPUT_MAP_KATAKANA; else if (strcmp(map_name, "walphabet") == 0) map_no = ANTHY_INPUT_MAP_WALPHABET; else if (strcmp(map_name, "hankaku_kana") == 0) map_no = ANTHY_INPUT_MAP_HANKAKU_KANA; else { send_error(); return; } anthy_input_map_select(ictx, map_no); send_ok(); } static void cmdh_get_candidate(struct anthy_input_context* ictx, struct command* cmd) { struct anthy_input_segment* seg; int cand_no; cand_no = atoi(cmd->arg[0]); seg = anthy_input_get_candidate(ictx, cand_no); if (seg == NULL) send_error(); else { send_single_candidate(seg); anthy_input_free_segment(seg); } } static void cmdh_select_candidate(struct anthy_input_context* ictx, struct command* cmd) { int ret; int cand_no; cand_no = atoi(cmd->arg[0]); ret = anthy_input_select_candidate(ictx, cand_no); if (ret < 0) { send_error(); } else { cmdh_get_preedit(ictx); } } static void cmd_shift_arrow(struct anthy_input_context* ictx, struct command* cmd) { int lr = atoi(cmd->arg[0]); anthy_input_resize(ictx, lr); } static void cmd_arrow(struct anthy_input_context* ictx, struct command* cmd) { int lr = atoi(cmd->arg[0]); anthy_input_move(ictx, lr); } static void cmd_key(struct anthy_input_context* ictx, struct command* cmd) { anthy_input_str(ictx, cmd->arg[0]); } /* * コマンドをディスパッチする */ static void dispatch_command(struct anthy_input_context* ictx, struct command* cmd) { switch (cmd->cmd) { case CMDH_IGNORE_ICTXT: send_error(); break; case CMDH_PRINT_CONTEXT: /* Dirty implementation, would cause corrpution.*/ { anthy_context_t ac = anthy_input_get_anthy_context(ictx); if (ac) { anthy_print_context(ac); } } break; case CMDH_GETPREEDIT: cmdh_get_preedit(ictx); break; case CMDH_SELECT_CONTEXT: cmdh_select_input_context(cmd); break; case CMDH_RELEASE_CONTEXT: cmdh_release_input_context(cmd); break; case CMDH_MAP_EDIT: cmdh_map_edit(cmd); break; case CMDH_CHANGE_TOGGLE: cmdh_change_toggle(cmd); break; case CMDH_MAP_CLEAR: cmdh_map_clear(cmd); break; case CMDH_MAP_SELECT: cmdh_map_select(ictx, cmd); break; case CMDH_GET_CANDIDATE: cmdh_get_candidate(ictx, cmd); break; case CMDH_SELECT_CANDIDATE: cmdh_select_candidate(ictx, cmd); break; case CMDH_SET_BREAK_INTO_ROMAN: cmdh_set_break_into_roman(cmd); break; case CMDH_SET_PREEDIT_MODE: cmdh_set_preedit_mode(cmd); break; /* key commands follows */ case CMD_SPACE: anthy_input_space(ictx); break; case CMD_ENTER: anthy_input_commit(ictx); break; case CMD_BACKSPACE: anthy_input_erase_prev(ictx); break; case CMD_DELETE: anthy_input_erase_next(ictx); break; case CMD_UP: anthy_input_prev_candidate(ictx); break; case CMD_ESC: anthy_input_quit(ictx); break; case CMD_SHIFTARROW: cmd_shift_arrow(ictx, cmd); break; case CMD_ARROW: cmd_arrow(ictx, cmd); break; case CMD_KEY: cmd_key(ictx, cmd); break; case CMD_GOBOL: anthy_input_beginning_of_line(ictx); break; case CMD_GOEOL: anthy_input_end_of_line(ictx); break; case CMD_CUT: anthy_input_cut(ictx); break; } } static void main_loop(void) { struct anthy_input_context* ictx; struct command* cmd; while (1) { cmd = read_command(); if (!cmd) break; ictx = get_current_input_context(); dispatch_command(ictx, cmd); fflush(stdout); free_command(cmd); } } static void print_version(void) { printf(PACKAGE "-agent "VERSION "\n"); exit(0); } static void parse_args(int argc, char **argv) { int i; char *conffile = NULL, *dir = NULL, *dic = NULL; for (i = 1; i < argc; i++) { char *str = argv[i]; if (!strcmp("--version", str)) { print_version(); } else if (!strcmp("--anonymous", str)) { anonymous = 1; } else if (!strcmp("--egg", str)) { egg = 1; } else if (!strncmp("--personality=", str, 14)) { personality = &str[14]; } else if (!strcmp("--utf8", str)) { use_utf8 = 1; } else if (i < argc - 1) { char *arg = argv[i+1]; if (!strcmp("--dir", str)) { dir = arg; i++; } else if (!strcmp("--dic", str)) { dic = arg; i++; } else if (!strcmp("--conffile", str)) { conffile = arg; i++; } } } if (conffile) { anthy_conf_override("CONFFILE", conffile); } if (dir) { anthy_conf_override("ANTHYDIR", dir); } if (dic) { anthy_conf_override("SDIC", dic); } } int main(int argc, char **argv) { parse_args(argc, argv); if (anthy_input_init()) { printf("Failed to init anthy\n"); exit(0); } if (anonymous) { anthy_input_set_personality(""); } else if (personality) { anthy_input_set_personality(personality); } if (egg) { egg_main(); anthy_quit(); } else { config = anthy_input_create_config(); conn = (struct connection*) malloc(sizeof(struct connection)); conn->rbuf = NULL; conn->n_rbuf = 0; conn->s_rbuf = 0; conn->rfd = 0; conn->wbuf = NULL; conn->n_wbuf = 0; conn->s_wbuf = 0; conn->wfd = 1; main_loop(); } return 0; }