t- New config variable "Encoding" added to override the locale's codeset - New "convert" module added to translate strings between codesets; all old code in gtk_client.c and configfile.c changed to use this API - UTF8 ability added to network code; as yet, it is non-functional, but will be used to translate network messages to and from UTF8 if necessary - vaccinewars - be a doctor and try to vaccinate the world
 (HTM) git clone git://src.adamsgaard.dk/vaccinewars
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 203e30e33f384b52c4b4d60050b9722c6d876d90
 (DIR) parent 693a111612284494085ee70284eb8cc05155e689
 (HTM) Author: Ben Webb <ben@salilab.org>
       Date:   Mon,  5 Aug 2002 11:35:44 +0000
       
       - New config variable "Encoding" added to override the locale's codeset
       - New "convert" module added to translate strings between codesets; all
         old code in gtk_client.c and configfile.c changed to use this API
       - UTF8 ability added to network code; as yet, it is non-functional, but
         will be used to translate network messages to and from UTF8 if necessary
       
       
       Diffstat:
         M doc/configfile.html                 |      11 ++++++++++-
         M src/Makefile.am                     |       2 +-
         M src/configfile.c                    |      82 ++++++++++++++++++++-----------
         M src/configfile.h                    |       1 +
         A src/convert.c                       |     136 +++++++++++++++++++++++++++++++
         A src/convert.h                       |      45 +++++++++++++++++++++++++++++++
         M src/dopewars.c                      |       8 ++++++++
         M src/dopewars.h                      |       4 +++-
         M src/gui_client/gtk_client.c         |      63 ++-----------------------------
         M src/message.c                       |       1 +
       
       10 files changed, 262 insertions(+), 91 deletions(-)
       ---
 (DIR) diff --git a/doc/configfile.html b/doc/configfile.html
       t@@ -638,6 +638,15 @@ feature.)</dd>
        <dd>If TRUE, then the server will keep track of the price you paid for
        any drugs, and clients will display this information if available.</dd>
        
       +<dt><b>Encoding=<i>"UTF-8"</i></b></dt>
       +<dd>Specifies that any text in the configuration files is in <i>"UTF-8"</i>
       +encoding. This variable is only supported if dopewars is built on a Unix
       +system and linked against GLib version 2. This influences both how the file
       +is read and written. If this variable is set to a blank string (the default)
       +then it will be assumed that your configuration files are in the locale's
       +default encoding - e.g. ISO-8859-1 for C or POSIX locales, ISO-8859-2 for pl_PL,
       +ISO-8859-15 for es_ES@euro, or UTF-8 for en_GB.UTF-8.</dd>
       +
        <dt><b>Sounds.FightHit=<i>"hit.wav"</i></b></dt>
        <dd>Plays the sound file <i>"hit.wav"</i> when you fire a gun and hit your
        target. The sound must be installed on your system, and you must be using
       t@@ -653,7 +662,7 @@ can be configured in the same way are: <b>FightMiss</b>, <b>FightReload</b>,
        <li><a href="index.html">Main index</a></li>
        </ul>
        <p>
       -  Last update: <b>16-07-2002</b><br />
       +  Last update: <b>04-08-2002</b><br />
          Valid <a href="http://validator.w3.org/check/referer">XHTML 1.1</a>
        </p>
        </body>
 (DIR) diff --git a/src/Makefile.am b/src/Makefile.am
       t@@ -26,7 +26,7 @@ dopewars_DEPENDENCIES = @GUILIB@ @CURSESLIB@ @GTKPORTLIB@ @CURSESPORTLIB@ @INTLL
        
        bin_PROGRAMS = dopewars
        dopewars_SOURCES = admin.c admin.h AIPlayer.c AIPlayer.h util.c util.h \
       -                   configfile.c configfile.h \
       +                   configfile.c configfile.h convert.c convert.h \
                           dopewars.c dopewars.h error.c error.h log.c log.h \
                           message.c message.h network.c network.h nls.h \
                           serverside.c serverside.h sound.c sound.h \
 (DIR) diff --git a/src/configfile.c b/src/configfile.c
       t@@ -31,6 +31,7 @@
        #include <ctype.h>              /* For isprint */
        #include <glib.h>
        
       +#include "convert.h"            /* For Converter */
        #include "dopewars.h"           /* For struct GLOBALS etc. */
        #include "nls.h"                /* For _ function */
        #include "error.h"              /* For ErrStrFromErrno */
       t@@ -77,35 +78,12 @@ static void PrintEscaped(FILE *fp, gchar *str)
        }
        
        /*
       - * Converts the given string from UTF-8 to the locale's codeset. If the
       - * locale codeset already is UTF-8, returns a copy of the original
       - * string. The returned string is dynamically allocated, and should be
       - * later g_free'd by the caller.
       - */
       -static gchar *ToLocaleCodeset(const gchar *origstr)
       -{
       -#ifdef HAVE_GLIB2
       -  if (!g_get_charset(NULL)) {
       -    gchar *convstr = g_locale_from_utf8(origstr, -1, NULL, NULL, NULL);
       -    if (convstr) {
       -      return convstr;
       -    } else {
       -      return g_strdup("[Could not convert string from UTF8]");
       -    }
       -  } else {
       -    return g_strdup(origstr);
       -  }
       -#else
       -  return g_strdup(origstr);
       -#endif
       -}
       -
       -/*
         * Writes a single configuration file variable (identified by GlobalIndex
         * and StructIndex) to the specified file, in a format suitable for reading
         * back in (via. ParseNextConfig and friends).
         */
       -static void WriteConfigValue(FILE *fp, int GlobalIndex, int StructIndex)
       +static void WriteConfigValue(FILE *fp, Converter *conv, int GlobalIndex,
       +                             int StructIndex)
        {
          gchar *GlobalName;
        
       t@@ -133,19 +111,24 @@ static void WriteConfigValue(FILE *fp, int GlobalIndex, int StructIndex)
            gchar *convstr;
        
            fprintf(fp, "%s = \"", GlobalName);
       -    convstr = ToLocaleCodeset(*GetGlobalString(GlobalIndex, StructIndex));
       +    convstr = Conv_ToExternal(conv,
       +                              *GetGlobalString(GlobalIndex, StructIndex), -1);
            PrintEscaped(fp, convstr);
            g_free(convstr);
            fprintf(fp, "\"\n");
          } else if (Globals[GlobalIndex].StringList) {
            int i;
       +    gchar *convstr;
        
            fprintf(fp, "%s = { ", GlobalName);
            for (i = 0; i < *Globals[GlobalIndex].MaxIndex; i++) {
              if (i > 0)
                fprintf(fp, ", ");
              fputc('"', fp);
       -      PrintEscaped(fp, (*Globals[GlobalIndex].StringList)[i]);
       +      convstr = Conv_ToExternal(conv,
       +                                (*Globals[GlobalIndex].StringList)[i], -1);
       +      PrintEscaped(fp, convstr);
       +      g_free(convstr);
              fputc('"', fp);
            }
            fprintf(fp, " }\n");
       t@@ -209,18 +192,24 @@ static void ReadFileToString(FILE *fp, gchar *str, int matchlen)
        static void WriteConfigFile(FILE *fp)
        {
          int i, j;
       +  Converter *conv = Conv_New();
       +
       +  if (Encoding && Encoding[0]) {
       +    Conv_SetCodeset(conv, Encoding);
       +  }
        
          for (i = 0; i < NUMGLOB; i++) {
            if (Globals[i].Modified) {
              if (Globals[i].NameStruct[0]) {
                for (j = 1; j <= *Globals[i].MaxIndex; j++) {
       -          WriteConfigValue(fp, i, j);
       +          WriteConfigValue(fp, conv, i, j);
                }
              } else {
       -        WriteConfigValue(fp, i, 0);
       +        WriteConfigValue(fp, conv, i, 0);
              }
            }
          }
       +  Conv_Free(conv);
        }
        
        gboolean UpdateConfigFile(const gchar *cfgfile)
       t@@ -261,3 +250,38 @@ gboolean UpdateConfigFile(const gchar *cfgfile)
          g_free(defaultfile);
          return TRUE;
        }
       +
       +static void ConvertString(Converter *conv, gchar **str)
       +{
       +  AssignName(str, Conv_ToInternal(conv, *str, -1));
       +}
       +
       +void ConvertConfigFile(void)
       +{
       +  int i, j;
       +  struct GLOBALS *gvar;
       +  Converter *conv = Conv_New();
       +
       +  if (Encoding && Encoding[0]) {
       +    Conv_SetCodeset(conv, Encoding);
       +  }
       +
       +  for (i = 0; i < NUMGLOB; i++) {
       +    gvar = &Globals[i];
       +    if (gvar->StringVal) {
       +      if (gvar->StructListPt) {
       +        for (j = 1; j <= *gvar->MaxIndex; j++) {
       +          ConvertString(conv, GetGlobalString(i, j));
       +        }
       +      } else {
       +        ConvertString(conv, GetGlobalString(i, 0));
       +      }
       +    } else if (gvar->StringList) {
       +      for (j = 0; j < *gvar->MaxIndex; j++) {
       +        ConvertString(conv, (*gvar->StringList) + j);
       +      }
       +    }
       +  }
       +
       +  Conv_Free(conv);
       +}
 (DIR) diff --git a/src/configfile.h b/src/configfile.h
       t@@ -26,5 +26,6 @@
        #include <glib.h>
        
        gboolean UpdateConfigFile(const gchar *cfgfile);
       +void ConvertConfigFile(void);
        
        #endif /* __DP_CONFIGFILE_H__ */
 (DIR) diff --git a/src/convert.c b/src/convert.c
       t@@ -0,0 +1,136 @@
       +/************************************************************************
       + * convert.c      Codeset conversion functions                          *
       + * Copyright (C)  2002  Ben Webb                                        *
       + *                Email: ben@bellatrix.pcl.ox.ac.uk                     *
       + *                WWW: http://dopewars.sourceforge.net/                 *
       + *                                                                      *
       + * 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.                               *
       + ************************************************************************/
       +
       +#ifdef HAVE_CONFIG_H
       +# include <config.h>
       +#endif
       +
       +#include <string.h>
       +#include <glib.h>
       +
       +#include "convert.h"
       +
       +static gchar *int_codeset = NULL;
       +
       +static const gchar *FixedCodeset(const gchar *codeset)
       +{
       +  if (strcmp(codeset, "ANSI_X3.4-1968") == 0
       +      || strcmp(codeset, "ASCII") == 0) {
       +    return "ISO-8859-1";
       +  } else {
       +    return codeset;
       +  }
       +}
       +
       +void Conv_SetInternalCodeset(const gchar *codeset)
       +{
       +  g_free(int_codeset);
       +  int_codeset = g_strdup(FixedCodeset(codeset));
       +}
       +
       +static const gchar *GetLocaleCodeset(void)
       +{
       +#ifdef HAVE_GLIB2
       +  const gchar *codeset;
       +
       +  g_get_charset(&codeset);
       +  return FixedCodeset(codeset);
       +#else
       +  return "ISO-8859-1";
       +#endif
       +}
       +
       +Converter *Conv_New(void)
       +{
       +  Converter *conv;
       +
       +  conv = g_new(Converter, 1);
       +  conv->ext_codeset = g_strdup(GetLocaleCodeset());
       +  if (!int_codeset) {
       +    int_codeset = g_strdup(GetLocaleCodeset());
       +  }
       +  return conv;
       +}
       +
       +void Conv_Free(Converter *conv)
       +{
       +  g_free(conv->ext_codeset);
       +  g_free(conv);
       +}
       +
       +void Conv_SetCodeset(Converter *conv, const gchar *codeset)
       +{
       +  g_free(conv->ext_codeset);
       +  conv->ext_codeset = g_strdup(FixedCodeset(codeset));
       +}
       +
       +gboolean Conv_Needed(Converter *conv)
       +{
       +#ifdef HAVE_GLIB2
       +  return (strcmp(conv->ext_codeset, int_codeset) != 0
       +          || strcmp(int_codeset, "UTF-8") == 0);
       +#else
       +  return FALSE;
       +#endif
       +}
       +
       +static gchar *do_convert(const gchar *from_codeset, const gchar *to_codeset,
       +                         const gchar *from_str, size_t from_len)
       +{
       +#ifdef HAVE_GLIB2
       +  gchar *to_str;
       +
       +  if (strcmp(to_codeset, "UTF-8") == 0 && strcmp(from_codeset, "UTF-8") == 0) {
       +    const gchar *end;
       +
       +    to_str = g_strdup(from_str);
       +    if (!g_utf8_validate(to_str, -1, &end) && end) {
       +      *((gchar *)end) = '\0';
       +    }
       +    return to_str;
       +  } else {
       +    to_str = g_convert(from_str, from_len, to_codeset, from_codeset,
       +                       NULL, NULL, NULL);
       +    if (to_str) {
       +      return to_str;
       +    } else {
       +      return g_strdup("[?]");
       +    }
       +  }
       +#else
       +  if (from_len == -1) {
       +    return g_strdup(from_str);
       +  } else {
       +    return g_strndup(from_str, from_len);
       +  }
       +#endif
       +}
       +
       +gchar *Conv_ToExternal(Converter *conv, const gchar *int_str, size_t len)
       +{
       +  return do_convert(int_codeset, conv->ext_codeset, int_str, len);
       +}
       +
       +gchar *Conv_ToInternal(Converter *conv, const gchar *ext_str, size_t len)
       +{
       +  return do_convert(conv->ext_codeset, int_codeset, ext_str, len);
       +}
 (DIR) diff --git a/src/convert.h b/src/convert.h
       t@@ -0,0 +1,45 @@
       +/************************************************************************
       + * convert.h      Header file for codeset conversion functions          *
       + * Copyright (C)  2002  Ben Webb                                        *
       + *                Email: ben@bellatrix.pcl.ox.ac.uk                     *
       + *                WWW: http://dopewars.sourceforge.net/                 *
       + *                                                                      *
       + * 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.                               *
       + ************************************************************************/
       +
       +#ifndef __DP_CONVERT_H__
       +#define __DP_CONVERT_H__
       +
       +#ifdef HAVE_CONFIG_H
       +# include <config.h>
       +#endif
       +
       +#include <glib.h>
       +
       +typedef struct _Converter Converter;
       +struct _Converter {
       +  gchar *ext_codeset;
       +};
       +
       +void Conv_SetInternalCodeset(const gchar *codeset);
       +Converter *Conv_New(void);
       +void Conv_SetCodeset(Converter *conv, const gchar *codeset);
       +gboolean Conv_Needed(Converter *conv);
       +gchar *Conv_ToExternal(Converter *conv, const gchar *int_str, size_t len);
       +gchar *Conv_ToInternal(Converter *conv, const gchar *ext_str, size_t len);
       +void Conv_Free(Converter *conv);
       +
       +#endif /* __DP_CONVERT_H__ */
 (DIR) diff --git a/src/dopewars.c b/src/dopewars.c
       t@@ -80,6 +80,7 @@ unsigned Port = 7902;
        gboolean Sanitized, ConfigVerbose, DrugValue;
        gchar *HiScoreFile = NULL, *ServerName = NULL, *ConvertFile = NULL;
        gchar *ServerMOTD = NULL, *WantedPlugin = NULL, *BindAddress = NULL;
       +gchar *Encoding = NULL;
        gboolean WantHelp, WantVersion, WantAntique, WantColour, WantNetwork;
        gboolean WantConvert, WantAdmin;
        
       t@@ -226,6 +227,11 @@ struct GLOBALS Globals[] = {
          /* The following strings are the helptexts for all the options that can
           * be set in a dopewars configuration file, or in the server. See
           * doc/configfile.html for more detailed explanations. */
       +#ifdef HAVE_GLIB2
       +  {NULL, NULL, NULL, &Encoding, NULL, "Encoding",
       +   N_("Charset encoding of the config file (e.g. UTF-8)"), NULL, NULL,
       +   0, "", NULL, NULL, FALSE, 0},
       +#endif
          {&Port, NULL, NULL, NULL, NULL, "Port", N_("Network port to connect to"),
           NULL, NULL, 0, "", NULL, NULL, FALSE, 0},
          {NULL, NULL, NULL, &HiScoreFile, NULL, "HiScoreFile",
       t@@ -2331,9 +2337,11 @@ void SetupParameters(void)
          /* Set hard-coded default values */
          g_free(ServerName);
          g_free(ServerMOTD);
       +  g_free(Encoding);
          g_free(BindAddress);
          ServerName = g_strdup("localhost");
          ServerMOTD = g_strdup("");
       +  Encoding = g_strdup("");
          BindAddress = g_strdup("");
          g_free(WebBrowser);
          WebBrowser = g_strdup("/usr/bin/mozilla");
 (DIR) diff --git a/src/dopewars.h b/src/dopewars.h
       t@@ -66,6 +66,8 @@ typedef enum {
                                         * notation */
          A_DONEFIGHT,                  /* A fight is only considered over once the
                                         * client sends the server a C_DONE message */
       +  A_UTF8,                       /* All strings are sent over the network using
       +                                 * UTF-8 (Unicode) encoding */
          A_NUM                         /* N.B. Must be last */
        } AbilType;
        
       t@@ -169,7 +171,7 @@ extern int NumLocation, NumGun, NumCop, NumDrug, NumSubway, NumPlaying,
                   NumStoppedTo;
        extern int DebtInterest, BankInterest;
        extern gchar *HiScoreFile, *ServerName, *ConvertFile, *ServerMOTD,
       -             *WantedPlugin, *BindAddress;
       +             *WantedPlugin, *BindAddress, *Encoding;
        extern gboolean WantHelp, WantVersion, WantAntique, WantColour,
                        WantNetwork, WantConvert, WantAdmin;
        #ifdef CYGWIN
 (DIR) diff --git a/src/gui_client/gtk_client.c b/src/gui_client/gtk_client.c
       t@@ -28,6 +28,8 @@
        #include <ctype.h>
        #include <string.h>
        
       +#include "configfile.h"
       +#include "convert.h"
        #include "dopewars.h"
        #include "gtk_client.h"
        #include "message.h"
       t@@ -2117,64 +2119,6 @@ static void SetIcon(GtkWidget *window, gchar **xpmdata)
        #endif
        }
        
       -#ifdef HAVE_GLIB2
       -/*
       - * Converts a single string from the locale's codeset (e.g. ISO-8859-1)
       - * to UTF8. (If the locale codeset is already UTF8, and the string is
       - * invalid, truncate it at the first invalid character.)
       - */
       -static void ConvertString(gchar **str)
       -{
       -  gchar *utf8str;
       -  const gchar *end;
       -
       -  if (str && *str && **str) {
       -    if (g_get_charset(NULL)) {
       -      if (!g_utf8_validate(*str, strlen(*str), &end) && end) {
       -        *((gchar *)end) = '\0';
       -      }
       -    } else {
       -      utf8str = g_locale_to_utf8(*str, strlen(*str), NULL, NULL, NULL);
       -      if (utf8str) {
       -        AssignName(str, utf8str);
       -        g_free(utf8str);
       -      }
       -    }
       -  }
       -}
       -
       -/*
       - * Converts all configuration file strings from the locale's encoding to
       - * UTF-8. This is a bit of a hack, as ideally all strings would be in
       - * UTF-8 anyway, but we need to support old GTK+1 and curses builds,
       - * which are not Unicode-aware. (N.B. As the default location names, etc.
       - * are set before bind_text_domain_codeset is called, if the locale's
       - * encoding is not UTF-8, then these strings will not be UTF-8 either.)
       - */
       -static void ConvertToUTF8(void)
       -{
       -  int i, j;
       -  struct GLOBALS *gvar;
       -
       -  for (i = 0; i < NUMGLOB; i++) {
       -    gvar = &Globals[i];
       -    if (gvar->StringVal) {
       -      if (gvar->StructListPt) {
       -        for (j = 1; j <= *gvar->MaxIndex; j++) {
       -          ConvertString(GetGlobalString(i, j));
       -        }
       -      } else {
       -        ConvertString(GetGlobalString(i, 0));
       -      }
       -    } else if (gvar->StringList) {
       -      for (j = 0; j < *gvar->MaxIndex; j++) {
       -        ConvertString((*gvar->StringList) + j);
       -      }
       -    }
       -  }
       -}
       -#endif
       -
        static void make_tags(GtkTextView *textview)
        {
        #ifdef HAVE_GLIB2
       t@@ -2216,7 +2160,8 @@ gboolean GtkLoop(int *argc, char **argv[], gboolean ReturnOnFail)
           * all translations in this encoding here. */
          bind_textdomain_codeset(PACKAGE, "UTF-8");
        
       -  ConvertToUTF8();
       +  Conv_SetInternalCodeset("UTF-8");
       +  ConvertConfigFile();
          WantUTF8Errors(TRUE);
        #endif
        
 (DIR) diff --git a/src/message.c b/src/message.c
       t@@ -247,6 +247,7 @@ void InitAbilities(Player *Play)
          Play->Abil.Local[A_DRUGVALUE] = (DrugValue ? TRUE : FALSE);
          Play->Abil.Local[A_TSTRING] = TRUE;
          Play->Abil.Local[A_DONEFIGHT] = TRUE;
       +  Play->Abil.Local[A_UTF8] = FALSE;
        
          if (!Network) {
            for (i = 0; i < A_NUM; i++) {