gnugo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnugo-devel] socket support for Windows: patch


From: Paul Pogonyshev
Subject: [gnugo-devel] socket support for Windows: patch
Date: Tue, 14 Dec 2004 02:00:08 +0200
User-agent: KMail/1.4.3

This patch (not ready yet) adds socket support for Windows.  It
has been checked by Vadim Lyakhovsky, but the patch is written
by me (with his advises) so there must be no copyright problems.

The patch is not yet finished.  Currently, `play_gtp.c' calls a
few functions with `gtp_output_file' as argument.  Unfortunately,
since now socket output can go not only over stdio streams, this
solution doesn't work anymore.

I can come up with only one idea: pass a function pointer
(fprintf() or gtp_printf()) instead of file.  Or, rather, in
addition to file, since the functions are called from other
places too.  Can anyone suggest something more elegant?

Also, the goal of Vadim Lyakhovsky (compile GNU Go without
changes for Pocket PC) has not been reached yet, though there
are a few moves in that direction.  I think we should complete
the patch for desktop Windows first.

Paul



Index: interface/gtp.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/gtp.c,v
retrieving revision 1.20
diff -u -p -r1.20 gtp.c
--- interface/gtp.c     11 Oct 2004 21:03:28 -0000      1.20
+++ interface/gtp.c     13 Dec 2004 23:49:03 -0000
@@ -43,6 +43,10 @@
 #include <ctype.h>
 #include <assert.h>
 
+#if defined(WIN32) || defined(_WIN32_WCE)
+#include <winsock.h>
+#endif
+
 #include "gtp.h"
 
 /* These are copied from gnugo.h. We don't include this file in order
@@ -69,17 +73,163 @@ static gtp_transform_ptr vertex_transfor
  */
 static int current_id;
 
-/* The file all GTP output goes to.  This is made global for the user
- * of this file may want to use functions other than gtp_printf() etc.
- * Set by gtp_main_loop().
- */
-FILE *gtp_output_file = NULL;
+/* The file GTP commands are read from. */
+static FILE *gtp_input_file;
+
+/* The file all GTP output goes to. */
+static FILE *gtp_output_file;
+
+#if defined(WIN32) || defined(_WIN32_WCE)
+/* The socket to receieve GTP commands and send responses on. */
+static int gtp_socket = 0;
+#endif
+
+static char * (* gtp_internal_gets) (char *buffer, int size) = NULL;
+static int (* gtp_internal_putc) (int character) = NULL;
+static int (* gtp_internal_puts) (const char *string) = NULL;
+static int (* gtp_internal_vprintf) (const char *format_string,
+                                    va_list arguments) = NULL;
+
+
+static int
+gtp_internal_printf(const char *format_string, ...)
+{
+  va_list arguments;
+  int result;
+
+  va_start(arguments, format_string);
+  result = gtp_internal_vprintf(format_string, arguments);
+  va_end(arguments);
+
+  return result;
+}
+
+
+static char *
+gtp_internal_gets_file(char *buffer, int size)
+{
+  return fgets(buffer, size, gtp_input_file);
+}
+
+
+static int
+gtp_internal_putc_file(int character)
+{
+  return putc(character, gtp_output_file);
+}
+
+
+static int
+gtp_internal_puts_file(const char *string)
+{
+  return fputs(string, gtp_output_file);
+}
+
+
+static int
+gtp_internal_vprintf_file(const char *format_string, va_list arguments)
+{
+  return vfprintf(gtp_output_file, format_string, arguments);
+}
+
+
+void
+gtp_use_file_io(FILE *gtp_input, FILE *gtp_output)
+{
+  /* Make sure `gtp_output' is unbuffered. (Line buffering is also
+   * okay but not necessary. Block buffering breaks GTP mode.)
+   */
+#if !defined(_WIN32_WCE)
+  setbuf(gtp_output, NULL);
+#endif
+
+  gtp_input_file  = gtp_input;
+  gtp_output_file = gtp_output;
+
+  gtp_internal_gets    = gtp_internal_gets_file;
+  gtp_internal_putc    = gtp_internal_putc_file;
+  gtp_internal_puts    = gtp_internal_puts_file;
+  gtp_internal_vprintf = gtp_internal_vprintf_file;
+}
+
+
+#if defined(WIN32) || defined(_WIN32_WCE)
+
+static char *
+gtp_internal_gets_socket(char *buffer, int size)
+{
+  /* FIXME: Optimize if reading char-by-char is too slow. */
+  int stored_length;
+
+  for (stored_length = 0; stored_length < size - 1; stored_length) {
+    if (recv(gtp_socket, buffer + stored_length, 1, 0) != 1)
+      break;
+
+    if (buffer[stored_length++] == '\n')
+      break;
+  }
+
+  if (stored_length == 0)
+    return NULL;
+
+  buffer[stored_length + 1] = 0;
+  return buffer;
+}
+
+
+static int
+gtp_internal_putc_socket(int character)
+{
+  return (send(gtp_socket, (const char *) &character, 1, 0) == 1
+         ? character : EOF);
+}
+
+
+static int
+gtp_internal_puts_socket(const char *string)
+{
+  int length = strlen(string);
+  return send(gtp_socket, string, length, 0) == length ? length : EOF;
+}
+
+
+static int
+gtp_internal_vprintf_socket(const char *format_string, va_list arguments)
+{
+  static char buffer[0x1000];
+  int length = _vsnprintf(buffer, sizeof buffer, format_string, arguments);
+
+  return send(gtp_socket, buffer, length, 0) == length ? length : -1;
+}
+
+
+void
+gtp_use_socket_io(int _gtp_socket)
+{
+  gtp_socket = _gtp_socket;
+
+  gtp_internal_gets    = gtp_internal_gets_socket;
+  gtp_internal_putc    = gtp_internal_putc_socket;
+  gtp_internal_puts    = gtp_internal_puts_socket;
+  gtp_internal_vprintf = gtp_internal_vprintf_socket;
+}
+
+#endif /* defined WIN32 or defined _WIN32_WCE */
+
+
+void
+gtp_flush(void)
+{
+#if defined(WIN32) || defined(_WIN32_WCE)
+  if (!gtp_socket)
+#endif
+    fflush(gtp_output_file);
+}
 
 
 /* Read filehandle gtp_input linewise and interpret as GTP commands. */
 void
-gtp_main_loop(struct gtp_command commands[],
-             FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands)
+gtp_main_loop(struct gtp_command commands[], FILE *gtp_dump_commands)
 {
   char line[GTP_BUFSIZE];
   char command[GTP_BUFSIZE];
@@ -88,11 +238,9 @@ gtp_main_loop(struct gtp_command command
   int n;
   int status = GTP_OK;
 
-  gtp_output_file = gtp_output;
-
   while (status == GTP_OK) {
     /* Read a line from gtp_input. */
-    if (!fgets(line, GTP_BUFSIZE, gtp_input))
+    if (!gtp_internal_gets(line, GTP_BUFSIZE))
       break; /* EOF or some error */
 
     if (gtp_dump_commands) {
@@ -189,25 +337,25 @@ gtp_mprintf(const char *fmt, ...)
       {
        /* rules of promotion => passed as int, not char */
        int c = va_arg(ap, int);
-       putc(c, gtp_output_file);
+       gtp_internal_putc(c);
        break;
       }
       case 'd':
       {
        int d = va_arg(ap, int);
-       fprintf(gtp_output_file, "%d", d);
+       gtp_internal_printf("%d", d);
        break;
       }
       case 'f':
       {
        double f = va_arg(ap, double); /* passed as double, not float */
-       fprintf(gtp_output_file, "%f", f);
+       gtp_internal_printf("%f", f);
        break;
       }
       case 's':
       {
        char *s = va_arg(ap, char *);
-       fputs(s, gtp_output_file);
+       gtp_internal_puts(s);
        break;
       }
       case 'm':
@@ -221,21 +369,21 @@ gtp_mprintf(const char *fmt, ...)
       {
        int color = va_arg(ap, int);
        if (color == WHITE)
-         fputs("white", gtp_output_file);
+         gtp_internal_puts("white");
        else if (color == BLACK)
-         fputs("black", gtp_output_file);
+         gtp_internal_puts("black");
        else
-         fputs("empty", gtp_output_file);
+         gtp_internal_puts("empty");
        break;
       }
       default:
        /* FIXME: Should go to `stderr' instead? */
-       fprintf(gtp_output_file, "\n\nUnknown format character '%c'\n", *fmt);
+       gtp_internal_printf("\n\nUnknown format character '%c'\n", *fmt);
        break;
       }
     }
     else
-      putc(*fmt, gtp_output_file);
+      gtp_internal_putc(*fmt);
   }
   va_end(ap);
 }
@@ -247,7 +395,7 @@ gtp_printf(const char *format, ...)
 {
   va_list ap;
   va_start(ap, format);
-  vfprintf(gtp_output_file, format, ap);
+  gtp_internal_vprintf(format, ap);
   va_end(ap);
 }
 
@@ -288,7 +436,7 @@ gtp_success(const char *format, ...)
   va_list ap;
   gtp_start_response(GTP_SUCCESS);
   va_start(ap, format);
-  vfprintf(gtp_output_file, format, ap);
+  gtp_internal_vprintf(format, ap);
   va_end(ap);
   return gtp_finish_response();
 }
@@ -301,7 +449,7 @@ gtp_failure(const char *format, ...)
   va_list ap;
   gtp_start_response(GTP_FAILURE);
   va_start(ap, format);
-  vfprintf(gtp_output_file, format, ap);
+  gtp_internal_vprintf(format, ap);
   va_end(ap);
   return gtp_finish_response();
 }
Index: interface/gtp.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/gtp.h,v
retrieving revision 1.16
diff -u -p -r1.16 gtp.h
--- interface/gtp.h     11 Oct 2004 21:03:28 -0000      1.16
+++ interface/gtp.h     13 Dec 2004 23:49:03 -0000
@@ -65,8 +65,15 @@ struct gtp_command {
   gtp_fn_ptr function;
 };
 
-void gtp_main_loop(struct gtp_command commands[],
-                  FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands);
+void gtp_use_file_io(FILE *gtp_input, FILE *gtp_output);
+
+#if defined(WIN32) || defined(_WIN32_WCE)
+void gtp_use_socket_io(int socket);
+#endif
+
+void gtp_flush(void);
+
+void gtp_main_loop(struct gtp_command commands[], FILE *gtp_dump_commands);
 void gtp_internal_set_boardsize(int size);
 void gtp_set_vertex_transform_hooks(gtp_transform_ptr in,
                                    gtp_transform_ptr out);
@@ -83,8 +90,6 @@ int gtp_decode_move(char *s, int *color,
 void gtp_print_vertices(int n, int movei[], int movej[]);
 void gtp_print_vertex(int i, int j);
 
-extern FILE *gtp_output_file;
-
 /*
  * Local Variables:
  * tab-width: 8
Index: interface/interface.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/interface.h,v
retrieving revision 1.17
diff -u -p -r1.17 interface.h
--- interface/interface.h       24 Nov 2004 04:54:08 -0000      1.17
+++ interface/interface.h       13 Dec 2004 23:49:03 -0000
@@ -33,8 +33,14 @@
 
 void play_ascii(SGFTree *tree, Gameinfo *gameinfo, 
                char *filename, char *until);
+
 void play_gtp(FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands,
              int gtp_initial_orientation);
+#if defined(WIN32) || defined(_WIN32_WCE)
+void play_gtp_with_socket(int _gtp_socket, FILE *gtp_dump_commands,
+                         int gtp_initial_orientation);
+#endif
+
 void play_gmp(Gameinfo *gameinfo, int simplified);
 void play_solo(Gameinfo *gameinfo, int benchmark);
 void play_replay(Gameinfo *gameinfo, int color_to_test);
Index: interface/main.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/main.c,v
retrieving revision 1.109
diff -u -p -r1.109 main.c
--- interface/main.c    7 Dec 2004 04:50:02 -0000       1.109
+++ interface/main.c    13 Dec 2004 23:49:14 -0000
@@ -54,16 +54,22 @@
 #include "sgftree.h"
 #include "random.h"
 
+/* Cludge. */
+#ifdef _WIN32_WCE
+#undef WIN32
+#define WIN32
+#endif
+
 static void show_copyright(void);
 static void show_version(void);
 static void show_help(void);
 static void show_debug_help(void);
 static void show_debug_flags(void);
 
-static void socket_connect_to(const char *host_name, unsigned int port,
-                             FILE **input_file, FILE **output_file);
-static void socket_listen_at(const char *host_name, unsigned int port,
+static int socket_connect_to(const char *host_name, unsigned int port,
                             FILE **input_file, FILE **output_file);
+static int socket_listen_at(const char *host_name, unsigned int port,
+                           FILE **input_file, FILE **output_file);
 static void socket_close_connection(FILE *input_file, FILE *output_file);
 static void socket_stop_listening(FILE *input_file, FILE *output_file);
 
@@ -1361,6 +1367,7 @@ main(int argc, char *argv[])
       FILE *gtp_input_FILE = stdin;
       FILE *gtp_output_FILE = stdout;
       FILE *gtp_dump_commands_FILE = NULL;
+      int gtp_socket;
 
       if (gtpfile != NULL) {
        gtp_input_FILE = fopen(gtpfile, "r");
@@ -1389,12 +1396,12 @@ main(int argc, char *argv[])
        }
 
        if (gtp_tcp_ip_mode == OPT_GTP_CONNECT) {
-         socket_connect_to(host_name, port,
-                           &gtp_input_FILE, &gtp_output_FILE);
+         gtp_socket = socket_connect_to(host_name, port,
+                                        &gtp_input_FILE, &gtp_output_FILE);
        }
        else {
-         socket_listen_at(host_name, port,
-                          &gtp_input_FILE, &gtp_output_FILE);
+         gtp_socket = socket_listen_at(host_name, port,
+                                       &gtp_input_FILE, &gtp_output_FILE);
        }
       }
 
@@ -1407,16 +1414,31 @@ main(int argc, char *argv[])
        }
       }
 
+#ifndef WIN32
+
       play_gtp(gtp_input_FILE, gtp_output_FILE, gtp_dump_commands_FILE,
               orientation);
 
-      if (gtp_dump_commands_FILE)
-       fclose(gtp_dump_commands_FILE);
-
       if (gtp_tcp_ip_mode == OPT_GTP_CONNECT)
        socket_close_connection(gtp_input_FILE, gtp_output_FILE);
       else if (gtp_tcp_ip_mode == OPT_GTP_LISTEN)
        socket_stop_listening(gtp_input_FILE, gtp_output_FILE);
+
+#else  /* defined WIN32  */
+
+      if (gtp_tcp_ip_mode != 0) {
+       play_gtp_with_socket(gtp_socket, gtp_dump_commands_FILE, orientation);
+       closesocket(gtp_socket);
+      }
+      else {
+       play_gtp(gtp_input_FILE, gtp_output_FILE, gtp_dump_commands_FILE,
+                orientation);
+      }
+
+#endif /* defined WIN32 */
+
+      if (gtp_dump_commands_FILE)
+       fclose(gtp_dump_commands_FILE);
     }
 
     break;
@@ -1686,14 +1708,23 @@ show_copyright(void)
 
 #if ENABLE_SOCKET_SUPPORT
 
+#ifndef WIN32
 
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 
+#define closesocket            close
 
-static void
+#else  /* defined WIN32 */
+
+#include <winsock.h>
+
+#endif /* defined WIN32 */
+
+
+static int
 socket_connect_to(const char *host_name, unsigned int port,
                  FILE **input_file, FILE **output_file)
 {
@@ -1702,6 +1733,18 @@ socket_connect_to(const char *host_name,
   struct hostent *host_data;
   char **address_pointer;
 
+#ifdef WIN32
+
+  WSADATA data;
+  WORD version = MAKEWORD(1, 1);
+
+  if (WSAStartup(version, &data) != NO_ERROR) {
+    fprintf(stderr, "WSAStartup() failed with error %d\n", WSAGetLastError());
+    exit(EXIT_FAILURE);
+  }
+
+#endif
+
   if (!host_name)
     host_name = "127.0.0.1";
 
@@ -1732,16 +1775,24 @@ socket_connect_to(const char *host_name,
 
   if (! *address_pointer) {
     fprintf(stderr, "Failed to connect to %s:%d\n", host_data->h_name, port);
-    close(connection_socket);
+    closesocket(connection_socket);
     exit(EXIT_FAILURE);
   }
 
+#ifndef WIN32
+
   *input_file  = fdopen(connection_socket, "r");
   *output_file = fdopen(dup(connection_socket), "w");
+
+  return 0;
+
+#else
+  return connection_socket;
+#endif
 }
 
 
-static void
+static int
 socket_listen_at(const char *host_name, unsigned int port,
                 FILE **input_file, FILE **output_file)
 {
@@ -1749,6 +1800,18 @@ socket_listen_at(const char *host_name, 
   int listening_socket;
   int connection_socket;
 
+#ifdef WIN32
+
+  WSADATA data;
+  WORD version = MAKEWORD(1, 1);
+
+  if (WSAStartup(version, &data) != NO_ERROR) {
+    fprintf(stderr, "WSAStartup() failed with error %d\n", WSAGetLastError());
+    exit(EXIT_FAILURE);
+  }
+
+#endif
+
   if (host_name) {
     struct hostent *host_data;
 
@@ -1794,14 +1857,22 @@ socket_listen_at(const char *host_name, 
     else
       fprintf(stderr, "Failed to listen on port %d\n", port);
 
-    close(listening_socket);
+    closesocket(listening_socket);
     exit(EXIT_FAILURE);
   }
 
-  close(listening_socket);
+  closesocket(listening_socket);
+
+#ifndef WIN32
 
   *input_file  = fdopen(connection_socket, "r");
   *output_file = fdopen(dup(connection_socket), "w");
+
+  return 0;
+
+#else
+  return connection_socket;
+#endif 
 }
 
 
@@ -1837,7 +1908,7 @@ socket_stop_listening(FILE *input_file, 
 #else  /* not ENABLE_SOCKET_SUPPORT */
 
 
-static void
+static int
 socket_connect_to(const char *host_name, unsigned int port,
                  FILE **input_file, FILE **output_file)
 {
@@ -1848,10 +1919,12 @@ socket_connect_to(const char *host_name,
 
   fprintf(stderr, "GNU Go was compiled without socket support, unable to 
connect\n");
   exit(EXIT_FAILURE);
+
+  return 0;
 }
 
 
-static void
+static int
 socket_listen_at(const char *host_name, unsigned int port,
                 FILE **input_file, FILE **output_file)
 {
@@ -1862,6 +1935,8 @@ socket_listen_at(const char *host_name, 
 
   fprintf(stderr, "GNU Go was compiled without socket support, unable to 
listen\n");
   exit(EXIT_FAILURE);
+
+  return 0;
 }
 
 
Index: interface/play_gtp.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/play_gtp.c,v
retrieving revision 1.162
diff -u -p -r1.162 play_gtp.c
--- interface/play_gtp.c        7 Dec 2004 17:16:54 -0000       1.162
+++ interface/play_gtp.c        13 Dec 2004 23:49:14 -0000
@@ -341,18 +341,9 @@ static struct gtp_command commands[] = {
 };
 
 
-/* Start playing using the Go Text Protocol. */
-void
-play_gtp(FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands,
-        int gtp_initial_orientation)
+static void
+gtp_initialize(int gtp_initial_orientation)
 {
-  /* Make sure `gtp_output' is unbuffered. (Line buffering is also
-   * okay but not necessary. Block buffering breaks GTP mode.)
-   *
-   * FIXME: Maybe should go to `gtp.c'?
-   */
-  setbuf(gtp_output, NULL);
-
   /* Inform the GTP utility functions about the board size. */
   gtp_internal_set_boardsize(board_size);
   gtp_orientation = gtp_initial_orientation;
@@ -364,12 +355,42 @@ play_gtp(FILE *gtp_input, FILE *gtp_outp
   /* Prepare pattern matcher and reading code. */
   reset_engine();
   clearstats();
-  gtp_main_loop(commands, gtp_input, gtp_output, gtp_dump_commands);
+}
+
+
+/* Start playing using the Go Text Protocol. */
+void
+play_gtp(FILE *gtp_input, FILE *gtp_output, FILE *gtp_dump_commands,
+        int gtp_initial_orientation)
+{
+  gtp_initialize(gtp_initial_orientation);
+
+  gtp_use_file_io(gtp_input, gtp_output);
+  gtp_main_loop(commands, gtp_dump_commands);
+
   if (showstatistics)
     showstats();
 }
 
 
+#if defined(WIN32) || defined(_WIN32_WCE)
+
+void
+play_gtp_with_socket(int socket, FILE *gtp_dump_commands,
+                    int gtp_initial_orientation)
+{
+  gtp_initialize(gtp_initial_orientation);
+
+  gtp_use_socket_io(socket);
+  gtp_main_loop(commands, gtp_dump_commands);
+
+  if (showstatistics)
+    showstats();
+}
+
+#endif
+
+
 /****************************
  * Administrative commands. *
  ****************************/
@@ -2733,8 +2754,8 @@ gtp_move_reasons(char *s)
     return gtp_failure("vertex must not be occupied");
 
   gtp_start_response(GTP_SUCCESS);
-  if (list_move_reasons(gtp_output_file, POS(i, j)) == 0)
-    gtp_printf("\n");
+/*   if (list_move_reasons(gtp_output_file, POS(i, j)) == 0) */
+/*     gtp_printf("\n"); */
   gtp_printf("\n");
   return GTP_OK;
 }
@@ -2753,7 +2774,7 @@ gtp_all_move_values(char *s)
 {
   UNUSED(s);
   gtp_start_response(GTP_SUCCESS);
-  print_all_move_values(gtp_output_file);
+/*   print_all_move_values(gtp_output_file); */
   gtp_printf("\n");
   return GTP_OK;
 }
@@ -3662,7 +3683,7 @@ gtp_showboard(char *s)
   
   gtp_start_response(GTP_SUCCESS);
   gtp_printf("\n");
-  simple_showboard(gtp_output_file);
+/*   simple_showboard(gtp_output_file); */
   return gtp_finish_response();
 }
 
@@ -4115,7 +4136,7 @@ gtp_dragon_data(char *s)
                && dragon[POS(m, n)].origin == POS(m, n))) {
          gtp_print_vertex(m, n);
          gtp_printf(":\n");
-         report_dragon(gtp_output_file, POS(m, n));
+/*       report_dragon(gtp_output_file, POS(m, n)); */
        }
   }
   gtp_printf("\n");
@@ -4391,7 +4412,7 @@ static int
 gtp_echo_err(char *s)
 {
   fprintf(stderr, "%s", s);
-  fflush(gtp_output_file);
+  gtp_flush();
   fflush(stderr);
   return gtp_success("%s", s);
 }





reply via email to

[Prev in Thread] Current Thread [Next in Thread]