diff Wnn/jserver/de.c @ 0:bbc77ca4def5

initial import
author Yoshiki Yazawa <yaz@cc.rim.or.jp>
date Thu, 13 Dec 2007 04:30:14 +0900
parents
children 790205f476c0
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Wnn/jserver/de.c	Thu Dec 13 04:30:14 2007 +0900
@@ -0,0 +1,1342 @@
+/*
+ * FreeWnn is a network-extensible Kana-to-Kanji conversion system.
+ * This file is part of FreeWnn.
+ * 
+ * Copyright Kyoto University Research Institute for Mathematical Sciences
+ *                 1987, 1988, 1989, 1990, 1991, 1992
+ * Copyright OMRON Corporation. 1987, 1988, 1989, 1990, 1991, 1992, 1999
+ * Copyright ASTEC, Inc. 1987, 1988, 1989, 1990, 1991, 1992
+ * Copyright FreeWnn Project 1999, 2000, 2001, 2002, 2003
+ *
+ * Maintainer:  FreeWnn Project   <freewnn@tomo.gr.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+        Jserver         (Nihongo Daemon)
+*/
+static char rcs_id[] = "$Id: de.c,v 1.36 2004/06/18 16:32:41 hiroo Exp $";
+
+#if defined(HAVE_CONFIG_H)
+#  include <config.h>
+#endif
+#include "getopt.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#if STDC_HEADERS
+#  include <stdlib.h>
+#  include <string.h>
+#else
+#  if HAVE_MALLOC_H
+#    include <malloc.h>
+#  endif
+#  if HAVE_STRINGS_H
+#    include <strings.h>
+#  endif
+#endif /* STDC_HEADERS */
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#if TIME_WITH_SYS_TIME
+#  include <sys/time.h>
+#  include <time.h>
+#else
+#  if HAVE_SYS_TIME_H
+#    include <sys/time.h>
+#  else
+#    include <time.h>
+#  endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+#if HAVE_UNISTD_H
+#  include <sys/types.h>
+#  include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#  include <fcntl.h>
+#endif
+#if HAVE_SYS_PARAM_H
+#  include <sys/param.h>
+#endif
+
+#include "getopt.h"
+
+#include "commonhd.h"
+#include "wnn_config.h"
+#include "jd_sock.h"
+#include "demcom.h"
+#include "wnn_os.h"
+#define GLOBAL_VALUE_DEFINE 1
+#include "de_header.h"
+#undef  GLOBAL_VALUE_DEFINE
+#include "msg.h"
+
+#ifdef SOLARIS
+#ifdef SO_DONTLINGER
+#undef SO_DONTLINGER
+#endif
+#endif /* SOLARIS */
+
+#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_NONBLOCK)
+#define USE_SETSOCKOPT 1
+#else
+#undef USE_SETSOCKOPT
+#endif
+
+#ifndef min
+#define min(x,y)        ( (x)<(y) ? (x) : (y) )
+#endif
+
+#ifndef INET6
+#  define OPTIONARGS  "Df:s:h:N:p:vu4"
+#else
+#  define OPTIONARGS  "Df:s:h:N:p:vu46"
+#endif /* INET6 */
+
+/*      Accept Socket   */
+#ifdef INET6
+#define MAX_ACCEPTS     3
+#else
+#define MAX_ACCEPTS     2
+#endif
+#define UNIX_ACPT       0
+#define INET_ACPT       1
+#ifdef INET6
+#define INET6_ACPT      2
+#endif
+
+#define PROTO_ALL	0x1
+#define PROTO_UN	0x2
+#define PROTO_INET	0x4
+#ifdef INET6
+#define PROTO_INET6	0x8
+#endif
+static int listen_proto = PROTO_ALL;
+
+jmp_buf client_dead;
+
+static int port;
+static int serverNO = 0;
+
+struct cmblk
+{
+  int sd;                       /** ソケットのfd        **/
+};
+#define COMS_BLOCK      struct  cmblk
+
+static COMS_BLOCK *cblk;
+                /** クライアントごとのソケットを管理するテーブル **/
+
+static COMS_BLOCK accept_blk[MAX_ACCEPTS];      /*accept sock blocks */
+
+
+/*      Communication Buffers           */
+static char snd_buf[R_BUF_SIZ];         /** 送信バッファ **/
+static int sbp;                         /** 送信バッファポインタ **/
+
+static int rbc;                         /** 受信バッファcounter **/
+static char rcv_buf[S_BUF_SIZ];         /** 受信バッファ **/
+
+#if defined(EAGAIN)
+# if defined(EWOULDBLOCK)
+# define ERRNO_CHECK(no)        ((no) == EAGAIN || (no) == EWOULDBLOCK)
+# else /* defined(EWOULDBLOCK) */
+# define ERRNO_CHECK(no)        ((no) == EAGAIN)
+# endif /* defined(EWOULDBLOCK) */
+#else /* defined(EAGAIN) */
+# if defined(EWOULDBLOCK)
+# define ERRNO_CHECK(no)        ((no) == EWOULDBLOCK)
+# else /* defined(EWOULDBLOCK) */
+# define ERRNO_CHECK(no)        (0)
+# endif /* defined(EWOULDBLOCK) */
+#endif /* defined(EAGAIN) */
+
+/*      Client Table    */
+int clientp;            /** cblkの有効なデータの最後を差している **/
+
+int cur_clp;            /** 現在のクライアントの番号 **/
+
+static fd_set *all_socks;	/** ビットパターン
+				    which jserver to select を保持 **/
+static fd_set *ready_socks;	/** データのきているソケットの
+				    ビットパターンを保持 **/
+static fd_set *dummy1_socks, *dummy2_socks;
+static int no_of_ready_socks;
+static int nofile;              /** No. of files **/
+struct msg_cat *wnn_msg_cat;
+struct msg_cat *js_msg_cat;
+
+/* function prototypes */
+static void daemon_main (void);
+static void socket_disc_init (void);
+static void sel_all (void);
+static int  get_client (void);
+static void new_client (void);
+static void daemon_init (void);
+static void daemon_fin_un (int);
+static void daemon_fin_in (int);
+static int  rcv_1_client (int);
+static void snd_1_client (int, int);
+static void socket_init_un (void);
+static void socket_init_in (void);
+static int  socket_accept_un (void);
+static int  socket_accept_in (int);
+static void xerror (char*);
+static void get_options (int, char **);
+static void usage (void);
+static void print_version (void);
+#ifdef DEBUG
+static void dmp (char*, int);
+#endif
+
+static char cmd_name[16];
+
+#if defined(HAVE_LIBWRAP)
+int allow_severity;
+int deny_severity;
+#  include <syslog.h>
+#  include <tcpd.h>
+#endif /* HAVE_LIBWRAP */
+
+/* No arguments are used. Only options. */
+int
+main (int argc, char *argv[])
+{
+  int tmpttyfd;
+  char *cswidth_name;
+  extern char *get_cswidth_name ();
+  extern void set_cswidth ();
+
+  char nlspath[64];
+
+  strcpy (cmd_name, WNN_DAEMON_NAME);
+  strcpy (lang_dir, LANG_NAME);
+  strcpy (nlspath, LIBDIR);
+  strcat (nlspath, "/%L/%N");
+  js_msg_cat = msg_open (MESSAGE_FILE, nlspath, lang_dir);
+  wnn_msg_cat = msg_open ("libwnn.msg", nlspath, lang_dir);
+  if (wnn_msg_cat == NULL)
+    {
+      log_err ("cannot open message file libwnn.msg.");
+    }
+  if (cswidth_name = get_cswidth_name (LANG_NAME))
+    set_cswidth (create_cswidth (cswidth_name));
+
+  port = -1;
+  /* option default */
+  option_flag = (OPT_FORK & ~OPT_VERBOSE);
+
+  setuid (geteuid ());
+  get_options (argc, argv);
+  print_version();
+  log_debug("invoked as %s.", argv[0]);
+  if (option_flag & OPT_FORK)
+    {
+      if (fork ())
+	{
+	  signal (SIGCHLD, _exit);
+	  signal (SIGHUP, SIG_IGN);
+	  signal (SIGINT, SIG_IGN);
+	  signal (SIGQUIT, SIG_IGN);
+#ifdef  SIGTSTP
+	  signal (SIGTSTP, SIG_IGN);
+#endif
+	  signal (SIGTERM, _exit);
+	  pause ();
+	}
+    }
+
+#if defined(HAVE_LIBWRAP)
+  allow_severity = LOG_INFO;
+  deny_severity = LOG_WARNING;
+  /*  hosts_access_verbose = 2; */
+#endif /* HAVE_LIBWRAP */
+
+  signal (SIGHUP, signal_hand);
+  signal (SIGINT, signal_hand);
+  signal (SIGQUIT, signal_hand);
+  signal (SIGTERM, terminate_hand);
+  if (option_flag & OPT_FORK)
+    {
+#ifdef  SIGTSTP
+      signal (SIGTSTP, SIG_IGN);
+#endif /* SIGTSTP */
+    }
+  read_default ();
+  daemon_init ();
+
+  env_init ();
+  if (file_init () == NULL)
+    {
+      exit (1);
+    }
+  dic_init ();
+  if (NULL == get_kaiseki_area (LENGTHCONV + 1))    /* 変換可能文字数 */
+    {
+      log_err ("get_kaiseki_area failed.");
+      exit (1);
+    }
+  init_work_areas ();
+  init_jmt ();
+
+  read_default_files ();
+
+  if (option_flag & OPT_FORK)
+    {
+      /* End of initialization, kill parent */
+      kill (getppid (), SIGTERM);
+      fclose (stdin);
+      fclose (stdout);
+      if (!(option_flag & OPT_VERBOSE))
+	{
+#if !(defined(BSD) && (BSD >= 199306))  /* !4.4BSD-Lite by Taoka */
+	  fclose (stderr);
+#else /* 4.4BSD-Lite */
+	  int fd = open ("/dev/null", O_WRONLY);
+	  if (fd < 0)
+	    {
+	      xerror ("Cannot open /dev/null");
+	    }
+	  dup2 (fd, 2);
+	  close (fd);
+#endif /* 4.4BSD-Lite */
+	}
+
+#ifdef SETPGRP_VOID
+      setpgrp ();
+#else /* !SETPGRP_VOID */
+# if !defined(TIOCNOTTY) && defined(SVR4)
+#  define TIOCNOTTY     _IO('t', 113)
+# endif /* !defined(TIOCNOTTY) && defined(SVR4) */
+#ifndef HITACHI
+      if ((tmpttyfd = open ("/dev/tty", O_RDWR)) >= 0)
+	{
+	  ioctl (tmpttyfd, TIOCNOTTY, 0);
+	  close (tmpttyfd);
+	}
+#endif /* HITACHI */
+#endif /* SETPGRP_VOID */
+    }
+
+  daemon_main ();
+
+  daemon_fin ();
+  return (0);	/* it is not reached. only for avoiding compiler warning. */
+}
+
+static void
+daemon_main (void)
+{
+  for (;;)
+    {
+      c_c = NULL;	/* Added for logging: server section */
+      sel_all ();
+      new_client ();
+      for (;;)
+        {
+          if (get_client () == -1)
+            break;
+          c_c = &client[cur_clp];
+          rbc = 0;
+          sbp = 0;
+/*      if(rcv_1_client(cur_clp) == 0){ del_client(); continue; }       */
+          if (setjmp (client_dead))
+            {
+              del_client ();
+              continue;
+            }
+          do_command (c_c);
+        }
+    }
+}
+
+/*
+        allocs area for selecting socketts
+*/
+static void
+socket_disc_init (void)
+{
+  if (WNN_NFD <= FD_SETSIZE)
+    {
+      nofile = WNN_NFD;
+    }
+  else
+    {
+      nofile = FD_SETSIZE;
+    }
+  all_socks = (fd_set *) malloc (sizeof (fd_set));
+  FD_ZERO (all_socks);
+  ready_socks = (fd_set *) malloc (sizeof (fd_set));
+  dummy1_socks = (fd_set *) malloc (sizeof (fd_set));
+  dummy2_socks = (fd_set *) malloc (sizeof (fd_set));
+}
+
+/**     全てのソケットについて待つ      **/
+static void
+sel_all (void)
+{
+  memcpy (ready_socks, all_socks, sizeof (fd_set));
+  bzero (dummy1_socks, sizeof (fd_set));
+  bzero (dummy2_socks, sizeof (fd_set));
+
+top:
+  errno = 0;
+  if ((no_of_ready_socks = select (nofile, ready_socks, dummy1_socks, dummy2_socks, NULL)) == -1)
+    {
+      if (errno == EINTR)
+        goto top;
+      xerror ("select error");
+    }
+#ifdef DEBUG
+  log_debug ("select OK, ready_socks[0]=%02X, n-r-s=%x\n", ready_socks[0], no_of_ready_socks);
+#endif
+}
+
+/**     ready_socksから今やる一人を取り出して返す(cur_clpにもセットする)
+        誰も居ない時は-1
+        スケジューリングはラウンドロビン        **/
+static int
+get_client (void)
+{
+  int i;
+
+  if (no_of_ready_socks == 0)
+    return -1;                  /* no client waits service */
+
+  for (i = cur_clp;;)
+    {
+      if (no_of_ready_socks == 0)
+        return -1;
+      i++;
+      if (i >= clientp)
+        i = 0;
+      if (FD_ISSET (cblk[i].sd, ready_socks))
+        {
+	  FD_CLR (cblk[i].sd, ready_socks);
+          no_of_ready_socks--;
+          return cur_clp = i;
+        }
+    }
+}
+
+/**     新しいクライアントが居るか否かを調べる
+        居た場合はcblkに登録する        **/
+static void
+new_client (void)               /* NewClient */
+{
+  int sd;
+  int full, i;
+  FILE *f[3];
+  char gomi[1024];
+#ifdef  HAVE_LIBWRAP
+  int is_internet_socket;
+  struct request_info tcpd_request;
+#endif /* HAVE_LIBWRAP */
+#ifdef  AF_UNIX
+  if ((serverNO == 0) &&
+      (FD_ISSET (accept_blk[UNIX_ACPT].sd, ready_socks)))
+    {
+      FD_CLR (accept_blk[UNIX_ACPT].sd, ready_socks);
+      no_of_ready_socks--;
+      sd = socket_accept_un ();
+#ifdef  HAVE_LIBWRAP
+      is_internet_socket = 0;
+#endif
+    }
+  else
+#endif
+#ifdef INET6
+  if (FD_ISSET (accept_blk[INET6_ACPT].sd, ready_socks))
+    {
+      FD_CLR (accept_blk[INET6_ACPT].sd, ready_socks);
+      no_of_ready_socks--;
+      sd = socket_accept_in (accept_blk[INET6_ACPT].sd);
+#ifdef  HAVE_LIBWRAP
+      is_internet_socket = 1;
+#endif
+    }
+  else
+#endif
+  if (FD_ISSET (accept_blk[INET_ACPT].sd, ready_socks))
+    {
+      FD_CLR (accept_blk[INET_ACPT].sd, ready_socks);
+      no_of_ready_socks--;
+      sd = socket_accept_in (accept_blk[INET_ACPT].sd);
+#ifdef  HAVE_LIBWRAP
+      is_internet_socket = 1;
+#endif
+    }
+  else
+    {
+      return;
+    }
+  log_debug ("new client: sd = %d", sd);
+  /* reserve 2 fd */
+  for (full = i = 0; i < 2; i++)
+    {
+      if (NULL == (f[i] = fopen ("/dev/null", "r")))
+        {
+          full = 1;
+        }
+    }
+  for (i = 0; i < 2; i++)
+    {
+      if (NULL != f[i])
+        fclose (f[i]);
+    }
+
+  if (full || sd >= nofile || clientp >= max_client)
+    {
+      log_err ("no more client.");
+#ifdef HAVE_RECV
+      recv (sd, gomi, 1024, 0);
+#else
+      read (sd, gomi, 1024);
+#endif
+      shutdown (sd, 2);
+#ifdef HAVE_CLOSESOCKET
+      closesocket (sd);
+#else
+      close (sd);
+#endif
+      return;
+    }
+
+#ifdef HAVE_LIBWRAP
+  if (is_internet_socket) {
+    request_init (&tcpd_request,RQ_DAEMON, WNN_DAEMON_NAME,
+		  RQ_FILE, sd, NULL);
+    fromhost (&tcpd_request);
+    if (!hosts_access (&tcpd_request))
+      {
+	log_err ("reject client."); /* should be log_info? */
+				    /* should we log IP address / hostname? */
+#ifdef HAVE_RECV
+	recv (sd, gomi, 1024, 0);
+#else
+	read (sd, gomi, 1024);
+#endif
+	shutdown (sd, 2);
+#ifdef HAVE_CLOSESOCKET
+	closesocket (sd);
+#else
+	close (sd);
+#endif
+	return;
+      }
+  }
+#endif /*  HAVE_LIBWRAP */
+
+  cblk[clientp].sd = sd;
+  FD_SET (sd, all_socks);
+  for (i = 0; i < WNN_MAX_ENV_OF_A_CLIENT; i++)
+    {
+      (client[clientp].env)[i] = -1;
+    }
+  clientp++;
+}
+
+/**     クライアントをcblkから削除する  **/
+/* delete Client (please call from JS_CLOSE) */
+void
+del_client (void)
+{
+  disconnect_all_env_of_client ();
+  FD_CLR (cblk[cur_clp].sd, all_socks);
+#ifdef HAVE_CLOSESOCKET
+  closesocket (cblk[cur_clp].sd);
+#else
+  close (cblk[cur_clp].sd);
+#endif
+  /* logging here because c_c (used in log_debug) will be broken after
+     following section */
+  log_debug("Delete Client: cur_clp = %d\n", cur_clp);
+  cblk[cur_clp] = cblk[clientp - 1];
+  client[cur_clp] = client[clientp - 1];
+  /* Clear host/user name with zero - needed for logging */
+  client[clientp - 1].user_name[0] = '\0';	/* Should we use bzero()? */
+  client[clientp - 1].host_name[0] = '\0';
+  clientp--;
+}
+
+
+/**     サーバをイニシャライズする      **/
+static void
+daemon_init (void)               /* initialize Daemon */
+{
+  /*
+  signal (SIGHUP, SIG_IGN);
+  signal (SIGINT, SIG_IGN);
+  signal (SIGQUIT, SIG_IGN);
+  */
+
+
+  if ((cblk = (COMS_BLOCK *) malloc (max_client * sizeof (COMS_BLOCK))) == NULL)
+    {
+      xerror ("daemon_init: ");
+    }
+  if ((client = (CLIENT *) malloc (max_client * sizeof (CLIENT))) == NULL)
+    {
+      xerror ("daemon_init: ");
+    }
+  SDRAND (time (NULL));
+  clientp = 0;                  /* V3.0 */
+  cur_clp = 0;                  /* V3.0 */
+  socket_disc_init ();
+#ifdef INET6
+  if (listen_proto&(PROTO_ALL|PROTO_INET|PROTO_INET6))
+      socket_init_in ();
+#else
+  if (listen_proto&(PROTO_ALL|PROTO_INET))
+      socket_init_in ();
+#endif
+#ifdef  AF_UNIX
+  if (listen_proto&(PROTO_ALL|PROTO_UN))
+      socket_init_un ();
+#endif /* AF_UNIX */
+}
+
+/**     サーバを終わる  **/
+#ifdef  AF_UNIX
+static void
+daemon_fin_un (int sock_d_un)
+{
+  int trueFlag = 1;
+  struct sockaddr_un addr_un;
+  socklen_t addrlen;
+
+  if (serverNO == 0)
+    {
+#ifndef SOLARIS
+#if defined(FIONBIO)
+      ioctl (sock_d_un, FIONBIO, &trueFlag);
+#endif
+#else /* !SOLARIS */
+      fcntl (sock_d_un, F_SETFL, F_UNLCK);
+#endif /* !SOLARIS */
+      for (;;)
+        {
+          addrlen = sizeof (addr_un);
+          if (accept (sock_d_un, (struct sockaddr *) &addr_un, &addrlen) < 0)
+            break;
+          /* EWOULDBLOCK EXPECTED, but we don't check */
+        }
+      shutdown (sock_d_un, 2);
+      close (sock_d_un);
+    }
+}
+#endif /* AF_UNIX */
+
+static void
+daemon_fin_in (int sock_d_in)
+{
+  int trueFlag = 1;
+  struct sockaddr_in addr_in;
+  socklen_t addrlen;
+#ifdef USE_SETSOCKOPT
+  int on = ~0;
+#endif
+
+#ifndef SOLARIS
+#ifdef USE_SETSOCKOPT
+  setsockopt (sock_d_in, SOL_SOCKET, SO_NONBLOCK, &on, sizeof (int));
+#else
+#if defined(FIONBIO)
+  ioctl (sock_d_in, FIONBIO, &trueFlag);
+#endif
+#endif /* USE_SETSOCKOPT */
+#else /* !SOLARIS */
+  fcntl (sock_d_in, F_SETFL, F_UNLCK);
+#endif /* !SOLARIS */
+  for (;;)
+    {
+      addrlen = sizeof (addr_in);
+      if (accept (sock_d_in, (struct sockaddr *) &addr_in, &addrlen) < 0)
+        break;
+      /* EWOULDBLOCK EXPECTED, but we don't check */
+    }
+  shutdown (sock_d_in, 2);
+#ifdef HAVE_CLOSESOCKET
+  closesocket (sock_d_in);
+#else
+  close (sock_d_in);
+#endif
+}
+
+void
+daemon_fin (void)
+{
+  int fd;
+#ifdef  AF_UNIX
+  int sock_d_un = accept_blk[UNIX_ACPT].sd;
+#endif /* AF_UNIX */
+  int sock_d_in = accept_blk[INET_ACPT].sd;
+#ifdef INET6
+  int sock_d_in6 = accept_blk[INET6_ACPT].sd;
+#endif
+
+  /*
+     accept all pending connection from new clients,
+     avoiding kernel hangup.
+   */
+#ifdef  AF_UNIX
+  daemon_fin_un (sock_d_un);
+#endif
+  daemon_fin_in (sock_d_in);
+#ifdef INET6
+  daemon_fin_in (sock_d_in6);
+#endif
+
+  for (fd = nofile - 1; fd >= 0; fd--)
+    {
+      if ((fd != sock_d_in) &&
+#ifdef INET6
+          (fd != sock_d_in6) &&
+#endif
+#ifdef AF_UNIX
+          (fd != sock_d_un) &&
+#endif /* AF_UNIX */
+          FD_ISSET (fd, all_socks))
+        {
+          shutdown (fd, 2);
+#ifdef HAVE_CLOSESOCKET
+          closesocket (fd);
+#else
+          close (fd);
+#endif
+        }
+    }
+}
+
+/*------*/
+
+/**     **/
+char *
+gets_cur (char *buffer, size_t buffer_size)
+{
+  char *b;
+
+  if (!buffer || !buffer_size)
+    return NULL;
+
+  b = buffer;
+
+  while (--buffer_size && (*b = getc_cur ()) != '\0')
+    b++;
+
+  if (!buffer_size)
+    {
+      *b = '\0';
+      while (getc_cur () != '\0')
+        ;
+    }
+
+  return buffer;
+}
+
+/**     **/
+w_char *
+getws_cur (w_char *buffer, size_t buffer_size)
+{
+  w_char *b;
+
+  if (!buffer || !buffer_size)
+    return NULL;
+
+  b = buffer;
+
+  while (--buffer_size && (*b = get2_cur ()) != 0)
+    b++;
+
+  if (!buffer_size)
+    {
+      *b = 0;
+      while (getc_cur () != 0)
+        ;
+    }
+
+  return buffer;
+}
+
+/**     カレント・クライアントから2バイト取る   **/
+int
+get2_cur (void)
+{
+  int x;
+  x = getc_cur ();
+  return (x << 8) | getc_cur ();
+}
+
+/**     カレント・クライアントから4バイト取る   **/
+int
+get4_cur (void)
+{
+  int x1, x2, x3;
+  x1 = getc_cur ();
+  x2 = getc_cur ();
+  x3 = getc_cur ();
+  return (x1 << (8 * 3)) | (x2 << (8 * 2)) | (x3 << (8 * 1)) | getc_cur ();
+}
+
+/**     カレント・クライアントから1バイト取る   **/
+int
+getc_cur (void)
+{
+  static int rbp;
+  if (rbc <= 0)
+    {
+      rbc = rcv_1_client (cur_clp);
+      rbp = 0;
+    }
+  rbc--;
+  return rcv_buf[rbp++] & 0xFF;
+}
+
+/**     クライアントから1パケット取る   **/
+static int
+rcv_1_client (int clp)		/* clp=クライアント番号 */
+{
+  int cc = 0;
+  while (cc <= 0)
+    {
+      errno = 0;
+#ifdef HAVE_RECV
+      cc = recv (cblk[clp].sd, rcv_buf, S_BUF_SIZ, 0);
+#else
+      cc = read (cblk[clp].sd, rcv_buf, S_BUF_SIZ);
+#endif
+      if (cc <= 0)
+        {
+          if (ERRNO_CHECK (errno))
+            {
+              continue;
+            }
+          else if (cc == 0)
+            {                   /* client dead */
+              longjmp (client_dead, 666);
+            }
+          else
+            {                   /* cc == -1 */
+              if (errno != EINTR)
+                longjmp (client_dead, 666);
+              continue;
+            }
+        }
+    }
+#ifdef DEBUG
+  log_debug ("rcv: clp = %d, sd = %d, cc = %d", clp, cblk[clp].sd, cc);
+  dmp (rcv_buf, cc);
+#endif
+  return cc;
+}
+
+/**     クライアントへ1パケット送る     **/
+static void
+snd_1_client (int clp,	/* clp: クライアント番号 */
+	      int n	/* n : number of bytes to send */ )
+{
+  int cc, x;
+#ifdef  DEBUG
+  log_debug ("snd: clp = %d, sd = %d", clp, cblk[clp].sd);
+  dmp (snd_buf, n);
+#endif
+  for (cc = 0; cc < n;)
+    {
+      errno = 0;
+#ifdef HAVE_SEND
+      x = send (cblk[clp].sd, &snd_buf[cc], n - cc, 0);
+#else
+      x = write (cblk[clp].sd, &snd_buf[cc], n - cc);
+#endif
+      if (x < 0)
+        {
+          if (ERRNO_CHECK (errno) || errno == EINTR)
+            {
+              errno = 0;
+              continue;
+            }
+          else
+            {                   /* client dead */
+              longjmp (client_dead, 666);
+            }
+        }
+      cc += x;
+    }
+}
+
+/**     **/
+void
+puts_cur (char *p)
+{
+  int c;
+  while (c = *p++)
+    putc_cur (c);
+  putc_cur (0);
+}
+
+/**     **/
+void
+puts_n_cur (char *p, int n)
+{
+  int c;
+  while ((c = *p++) && --n >= 0)
+    putc_cur (c);
+  putc_cur (0);
+}
+
+/**     **/
+void
+putws_cur (w_char *p)
+{
+  int c;
+  while (c = *p++)
+    put2_cur (c);
+  put2_cur (0);
+}
+
+/**     **/
+void
+putnws_cur (w_char *p, int n)
+{
+  int c;
+  for (; n > 0; n--)
+    {
+      if ((c = *p++) == 0)
+        break;
+      put2_cur (c);
+    }
+  put2_cur (0);
+}
+
+/**     カレント・クライアントへ2バイト送る     **/
+void
+put2_cur (int c)
+{
+  putc_cur (c >> (8 * 1));
+  putc_cur (c);
+}
+
+/**     カレント・クライアントへ4バイト送る     **/
+void
+put4_cur (int c)
+{
+  putc_cur (c >> (8 * 3));
+  putc_cur (c >> (8 * 2));
+  putc_cur (c >> (8 * 1));
+  putc_cur (c);
+}
+
+/**     カレント・クライアントへ1バイト送る     **/
+void
+putc_cur (int c)
+{
+  snd_buf[sbp++] = c;
+  if (sbp >= R_BUF_SIZ)
+    {
+      snd_1_client (cur_clp, R_BUF_SIZ);
+      sbp = 0;
+    }
+}
+
+/**     カレント・クライアントの送信バッファをフラッシュする    **/
+void
+putc_purge (void)
+{
+  if (sbp != 0)
+    {
+      snd_1_client (cur_clp, sbp);
+      sbp = 0;
+    }
+}
+
+/*-----*/
+
+/**     ソケットのイニシャライズ        **/
+#ifdef  AF_UNIX
+static void
+socket_init_un (void)
+{
+  struct sockaddr_un saddr_un;
+  int sock_d_un;
+  if (serverNO == 0)
+    {
+      saddr_un.sun_family = AF_UNIX;
+      unlink (sockname);
+      strcpy (saddr_un.sun_path, sockname);
+      if ((sock_d_un = socket (AF_UNIX, SOCK_STREAM, 0)) == ERROR)
+        {
+          xerror ("could not create unix domain socket");
+        }
+      if (bind (sock_d_un, (struct sockaddr *) &saddr_un, strlen (saddr_un.sun_path) + 2) == ERROR)
+        {
+          shutdown (sock_d_un, 2);
+          xerror ("could not bind unix domain socket");
+        }
+      if (listen (sock_d_un, 5) == ERROR)
+        {
+          shutdown (sock_d_un, 2);
+          xerror ("could not listen unix domain socket");
+        }
+      chmod (sockname, 0777);
+      signal (SIGPIPE, SIG_IGN);
+#ifdef DEBUG
+      log_debug ("sock_d_un = %d", sock_d_un);
+#endif
+      accept_blk[UNIX_ACPT].sd = sock_d_un;
+      FD_SET (sock_d_un, all_socks);
+    }
+}
+#endif /* AF_UNIX */
+
+/*      Inet V3.0 */
+static void
+socket_init_in (void)
+{
+#ifndef SOLARIS
+  int on = 1;
+#else /* SOLARIS */
+  int on = 0;
+#endif /* SOLARIS */
+  struct servent *sp;
+#if !defined(SO_DONTLINGER) && defined(SO_LINGER)
+  struct linger linger;
+#endif
+#ifdef INET6
+  struct addrinfo hints, *res, *res0;
+  int error;
+  char sport[6];
+#else
+  struct sockaddr_in saddr_in;
+#endif
+  int sock_d_in;
+
+  if (port < 0)
+    {
+      if ((sp = getservbyname (SERVERNAME, "tcp")) == NULL)
+        {
+          port = WNN_PORT_IN;
+        }
+      else
+        {
+          port = ntohs (sp->s_port);
+        }
+    }
+
+  port += serverNO;
+
+#if DEBUG
+  log_debug ("port=%x", port);
+#endif
+#ifdef INET6
+  memset(&hints, 0, sizeof(hints));
+  if (listen_proto&PROTO_INET && !(listen_proto&PROTO_INET6))
+      hints.ai_family = PF_INET;
+  else if (listen_proto&PROTO_INET6 && !(listen_proto&PROTO_INET))
+      hints.ai_family = PF_INET6;
+  else
+      hints.ai_family = PF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_PASSIVE;
+  sprintf(sport, "%d", port);
+  error = getaddrinfo(NULL, sport, &hints, &res0);
+  if (error)
+    {
+      xerror (gai_strerror(error));
+    }
+  for (res = res0; res; res = res->ai_next) {
+    if (res->ai_family == AF_INET || res->ai_family == AF_INET6){
+      if ((sock_d_in = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) == ERROR)
+#else
+  saddr_in.sin_family = AF_INET;
+  saddr_in.sin_port = htons (port);
+  saddr_in.sin_addr.s_addr = htonl (INADDR_ANY);
+  if ((sock_d_in = socket (AF_INET, SOCK_STREAM, 0)) == ERROR)
+#endif
+    {
+#ifdef INET6
+      if (res->ai_family == AF_INET6)
+        xerror ("could not create inet6 socket");
+      else if (res->ai_family == AF_INET)
+#endif
+      xerror ("could not create inet socket");
+    }
+  setsockopt (sock_d_in, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (int));
+#ifdef SO_DONTLINGER
+  setsockopt (sock_d_in, SOL_SOCKET, SO_DONTLINGER, (char *) 0, 0);
+#else
+# ifdef SO_LINGER
+  linger.l_onoff = 0;
+  linger.l_linger = 0;
+  setsockopt (sock_d_in, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof linger);
+# endif /* SO_LINGER */
+#endif /* SO_DONTLINGER */
+
+#ifdef INET6
+  if (bind (sock_d_in, res->ai_addr, res->ai_addrlen) == ERROR)
+#else
+  if (bind (sock_d_in, (struct sockaddr *) &saddr_in, sizeof (saddr_in)) == ERROR)
+#endif
+    {
+      shutdown (sock_d_in, 2);
+#ifdef INET6
+      if (res->ai_family == AF_INET6)
+        xerror ("can't bind inet6 socket");
+      else if (res->ai_family == AF_INET)
+#endif
+      xerror ("can't bind inet socket");
+    }
+  if (listen (sock_d_in, 5) == ERROR)
+    {
+      shutdown (sock_d_in, 2);
+#ifdef INET6
+      if (res->ai_family == AF_INET6)
+        xerror ("can't listen inet6 socket");
+      else if (res->ai_family == AF_INET)
+#endif
+      xerror ("can't listen inet socket");
+    }
+#if DEBUG
+  log_debug ("sock_d_in = %d", sock_d_in);
+#endif
+  FD_SET (sock_d_in, all_socks);
+#ifdef INET6
+      if (res->ai_family == AF_INET)
+	accept_blk[INET_ACPT].sd = sock_d_in;
+      else if (res->ai_family == AF_INET6)
+	accept_blk[INET6_ACPT].sd = sock_d_in;
+    }
+  }
+  freeaddrinfo(res0);
+#else
+  accept_blk[INET_ACPT].sd = sock_d_in;
+#endif
+}
+
+
+/**     accept new client socket        **/
+#ifdef  AF_UNIX
+static int
+socket_accept_un (void)
+{
+  struct sockaddr_un addr;
+  socklen_t addrlen;
+
+  addrlen = sizeof (addr);
+  return accept (accept_blk[UNIX_ACPT].sd, (struct sockaddr *) &addr, &addrlen);
+}
+#endif /* AF_UNIX */
+
+static int
+socket_accept_in (int fd)
+{
+  struct sockaddr_in addr;
+  socklen_t addrlen;
+
+  addrlen = sizeof (addr);
+  return accept (fd, (struct sockaddr *) &addr, &addrlen);
+}
+
+static void
+xerror (char *s)
+{
+  log_err ("%s (%s).", s, strerror(errno));
+  exit (1);
+}
+
+#if DEBUG
+static void
+dmp (char *p, int n)
+{
+  int i, j;
+
+  for (i = 0; i < n; i += 16)
+    {
+      for (j = 0; j < 16; j++)
+        {
+          fprintf (stderr, "%02x ", p[i + j] & 0xFF);
+        }
+      fprintf (stderr, "n=%d\n", n);
+    }
+}
+#endif
+
+static void
+get_options (int argc, char **argv)
+{
+  int c;
+  int digit_optind = 0;
+
+  strcpy (jserverrcfile, LIBDIR);       /* usr/local/lib/wnn */
+  strcat (jserverrcfile, SERVER_INIT_FILE);     /* ja_JP/jserverrc */
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+	{"baseport",	1, NULL, 'p'},
+	{"inet",	0, NULL, '4'},
+	{"inet6",	0, NULL, '6'},
+	{"jserverrc",	1, NULL, 'f'},
+	{"version",	0, NULL, 'v'},
+	{0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, OPTIONARGS,
+		       long_options, &option_index);
+      if (c == -1)
+        break;
+
+      switch (c)
+	{
+	case 'D': /* do not detach, not a daemon */
+	  option_flag &= ~OPT_FORK;
+	  break;
+
+        case 'f': /* --jserverrc FILENAME */
+          strcpy (jserverrcfile, optarg);
+          break;
+
+        case 's':
+	  /* should nuke noisy someday */
+          noisy = 1; option_flag |= OPT_VERBOSE;
+          if (strcmp ("-", optarg) != 0)
+            {
+	      /** maybe FILE wnnerr = stderr; or wnnerr = open(optarg...) is better? or freopen is normal method? */
+	      /** take a look at daemon(3) */
+              if (freopen (optarg, "a", stderr) == NULL)
+                {
+		  /** fprintf to stderr? */
+                  printf ("Error in opening scriptfile %s.\n", optarg);
+                  exit (1);
+                }
+            }
+          log_debug ("script started");
+          break;
+
+        case 'h':
+	  /* var hinsi_file_name polluted */
+          hinsi_file_name = optarg; 
+          break;
+
+        case 'N':
+          serverNO = atoi (optarg);
+	  /* error handling needed */
+          break;
+
+        case 'p':
+          port = atoi (optarg);
+	  /* error handling needed */
+          break;
+
+        case 'v':
+          print_version();
+          usage();
+	  break;
+
+        case 'u':
+	  listen_proto &= ~PROTO_ALL;
+	  listen_proto |= PROTO_UN;
+	  break;
+
+        case '4':
+	  listen_proto &= ~PROTO_ALL;
+	  listen_proto |= PROTO_INET;
+	  break;
+
+#ifdef INET6
+        case '6':
+	  listen_proto &= ~PROTO_ALL;
+	  listen_proto |= PROTO_INET6;
+          break;
+#endif /* INET6 */
+
+        default:
+          print_version();
+	  usage();
+	  break;
+	}
+    }
+}
+
+
+/*
+*/
+void
+js_who (void)
+{
+  int i, j;
+
+  put4_cur (clientp);
+  for (i = 0; i < clientp; i++)
+    {
+      put4_cur (cblk[i].sd);
+      puts_cur (client[i].user_name);
+      puts_cur (client[i].host_name);
+      for (j = 0; j < WNN_MAX_ENV_OF_A_CLIENT; j++)
+        {
+          put4_cur ((client[i].env)[j]);
+        }
+
+    }
+  putc_purge ();
+}
+
+void
+js_kill (void)
+{
+  if (clientp == 1)
+    {
+      put4_cur (0);
+      putc_purge ();
+      terminate_hand ();
+    }
+  else
+    {
+      put4_cur (clientp - 1);
+      putc_purge ();
+    }
+}
+
+void
+usage (void)
+{
+  fprintf(stderr, 
+#ifdef INET6
+	  "usage: %s [-Du46][-f <init_file> -s <log_file(\"-\" for stderr)> -h <pos_file> -N <serverNO> -p <port_base>]\n",
+#else
+	  "usage: %s [-Du4][-f <init_file> -s <log_file(\"-\" for stderr)> -h <pos_file> -N <serverNO> -p <port_base>]\n",
+#endif
+	  cmd_name);
+  fprintf(stderr,
+	  "       %s -v\n",
+	  cmd_name);
+  exit (1);
+}
+
+void
+print_version (void)
+{
+#if  defined(CHINESE)
+  printf ("%s (%s) Chinese Multi Client Server\n", cmd_name, SER_VERSION);
+#elif  defined(KOREAN)
+  printf ("%s (%s) Korean Multi Client Server\n", cmd_name, SER_VERSION);
+#else
+  printf ("%s (%s) Nihongo Multi Client Server\n", cmd_name, SER_VERSION);
+#endif /* CHINESE */
+}