Complete.Org: Mailing Lists: Archives: freeciv-dev: August 2002:
[Freeciv-Dev] client string options: choosing from a list (v2) (PR#1856)
Home

[Freeciv-Dev] client string options: choosing from a list (v2) (PR#1856)

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: freeciv-dev@xxxxxxxxxxx
Cc: bugs@xxxxxxxxxxxxxxxxxxx
Subject: [Freeciv-Dev] client string options: choosing from a list (v2) (PR#1856)
From: jdorje@xxxxxxxxxxxxxxxxxxxxx
Date: Thu, 1 Aug 2002 15:24:43 -0700 (PDT)

Here is a new version of the patch.

Changes:

- Fixed bugs when there are no available choices for
the strings.

- Changed some char** to const char**.

- Added #defines for ".tilespec" and ".spec",
simultaneously removing an arbitrary string-length
restriction of 16.  (As a side note, I wish it were
.sndspec instead of .spec for sound sets.  Changing
this should now just require changing the #define, but
backwards-compatability would be broken.)

- Instead of hard-coding the list of sound plugins,
loop over the available plugins to assemble the list.

- Removed the use of scandir().  glob() is now used
instead.  This is at least a POSIX standard, although
it may still not work on non-POSIX systems.  But what
alternatives are there?

jason


__________________________________________________
Do You Yahoo!?
Yahoo! Health - Feel better, live better
http://health.yahoo.com
Index: client/audio.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/audio.c,v
retrieving revision 1.11
diff -u -r1.11 audio.c
--- client/audio.c      2002/07/23 02:48:45     1.11
+++ client/audio.c      2002/08/01 22:20:11
@@ -17,12 +17,15 @@
 
 #include <string.h>
 #include <assert.h>
+#include <stdio.h>
 #include <stdlib.h>
 
+#include "audio.h"
 #include "support.h"
 #include "fcintl.h"
 #include "log.h"
 #include "capability.h"
+#include "mem.h"
 #include "shared.h"
 #include "registry.h"
 #include "audio_none.h"
@@ -46,6 +49,7 @@
 #include "audio.h"
 
 #define MAX_NUM_PLUGINS        3
+#define SNDSPEC_SUFFIX ".spec"
 
 /* keep it open throughout */
 static struct section_file tagstruct, *tagfile = &tagstruct;
@@ -54,6 +58,43 @@
 static int num_plugins_used = 0;
 static int selected_plugin = -1;
 
+/**********************************************************************
+  Returns a static, NULL-terminated list of all sound plugins
+  available on the system.  This function is unfortunately similar to
+  audio_get_all_plugin_names().
+***********************************************************************/
+const char **get_soundplugin_list(void)
+{
+  static const char* plugin_list[MAX_NUM_PLUGINS + 1];
+  int i;
+  
+  for (i = 0; i < num_plugins_used; i++) {
+    plugin_list[i] = plugins[i].name;
+  }
+  assert(i <= MAX_NUM_PLUGINS);
+  plugin_list[i] = NULL;
+
+  return plugin_list;
+}
+
+/**********************************************************************
+  Returns a static list of soundsets available on the system by
+  searching all data directories for files matching SNDSPEC_SUFFIX.
+  The list is NULL-terminated.
+***********************************************************************/
+const char **get_soundset_list(void)
+{
+  static const char **audio_list = NULL;
+
+  if (!audio_list) {
+    /* Note: this means you must restart the client after installing a new
+       soundset. */
+    audio_list = datafilelist(SNDSPEC_SUFFIX);
+  }
+
+  return audio_list;
+}
+
 /**************************************************************************
   Add a plugin.
 **************************************************************************/
@@ -128,6 +169,24 @@
 #endif
 }
 
+static char *soundspec_fullname(const char *soundset_name)
+{
+  char *soundset_default = "stdsounds"; /* Do not i18n! */
+  char *fname, *dname;
+  
+  fname = fc_malloc(strlen(soundset_name) + strlen(SNDSPEC_SUFFIX) + 1);
+  sprintf(fname, "%s" SNDSPEC_SUFFIX, soundset_name);
+
+  dname = datafilename(fname);
+  if (dname
+      || strcmp(soundset_name, soundset_default) == 0) {
+    free(fname);
+    return NULL;
+  }
+
+  return soundspec_fullname(soundset_default);
+}
+
 /**************************************************************************
   Initialize audio system and autoselect a plugin
 **************************************************************************/
@@ -161,7 +220,7 @@
     exit(EXIT_FAILURE);
   }
   freelog(LOG_VERBOSE, "Initializing sound using %s...", spec_name);
-  filename = datafilename(spec_name);
+  filename = soundspec_fullname(spec_name);
   if (!filename) {
     freelog(LOG_ERROR, _("Cannot find audio spec-file \"%s\"."), spec_name);
     freelog(LOG_ERROR, _("To get sound you need to download a sound set!"));
Index: client/audio.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/audio.h,v
retrieving revision 1.2
diff -u -r1.2 audio.h
--- client/audio.h      2002/04/23 22:51:22     1.2
+++ client/audio.h      2002/08/01 22:20:11
@@ -28,6 +28,9 @@
   bool (*play) (const char *const tag, const char *const path, bool repeat);
 };
 
+const char **get_soundplugin_list(void);
+const char **get_soundset_list(void);
+
 void audio_init(void);
 void audio_real_init(const char *const spec_name,
                     const char *const prefered_plugin_name);
Index: client/options.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/options.c,v
retrieving revision 1.62
diff -u -r1.62 options.c
--- client/options.c    2002/07/23 02:48:45     1.62
+++ client/options.c    2002/08/01 22:20:12
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "audio.h"
 #include "clinet.h"
 #include "events.h"
 #include "fcintl.h"
@@ -28,6 +29,7 @@
 #include "registry.h"
 #include "shared.h"
 #include "support.h"
+#include "tilespec.h"
 #include "version.h"
 #include "mem.h"
 
@@ -44,7 +46,7 @@
 int  default_server_port = DEFAULT_SOCK_PORT;
 char default_metaserver[512] = METALIST_ADDR;
 char default_tile_set_name[512] = "\0";
-char default_sound_set_name[512] = "stdsounds.spec";
+char default_sound_set_name[512] = "stdsounds";
 char default_sound_plugin_name[512] = "\0";
 
 /** Local Options: **/
@@ -66,22 +68,27 @@
 bool meta_accelerators = TRUE;
 
 #define GEN_INT_OPTION(name, desc) { #name, desc, COT_INT, \
-                                     &name, NULL, NULL, 0, NULL }
+                                     &name, NULL, NULL, 0, NULL, NULL }
 #define GEN_BOOL_OPTION(name, desc) { #name, desc, COT_BOOL, \
-                                      NULL, &name, NULL, 0, NULL }
-#define GEN_STR_OPTION(name, desc) { #name, desc, COT_STR, \
-                                     NULL, NULL, name, sizeof(name), NULL }
+                                      NULL, &name, NULL, 0, NULL, NULL }
+#define GEN_STR_OPTION(name, desc, dflt) { #name, desc, COT_STR, \
+                                     NULL, NULL, name, sizeof(name), \
+                                     dflt, NULL }
 #define GEN_OPTION_TERMINATOR { NULL, NULL, COT_BOOL, \
                                 NULL, NULL, NULL, 0, NULL }
 
 client_option options[] = {
-  GEN_STR_OPTION(default_player_name,       N_("Default player's username")), 
-  GEN_STR_OPTION(default_server_host,       N_("Default server")),
+  GEN_STR_OPTION(default_player_name,       N_("Default player's username"),
+                NULL), 
+  GEN_STR_OPTION(default_server_host,       N_("Default server"), NULL),
   GEN_INT_OPTION(default_server_port,       N_("Default server's port")),
-  GEN_STR_OPTION(default_metaserver,        N_("Default metaserver")),
-  GEN_STR_OPTION(default_tile_set_name,     N_("Default tileset")),
-  GEN_STR_OPTION(default_sound_set_name,    N_("Default name of sound set")),
-  GEN_STR_OPTION(default_sound_plugin_name, N_("Default sound plugin")),
+  GEN_STR_OPTION(default_metaserver,        N_("Default metaserver"), NULL),
+  GEN_STR_OPTION(default_tile_set_name,     N_("Default tileset"),
+                get_tileset_list),
+  GEN_STR_OPTION(default_sound_set_name,    N_("Default name of sound set"),
+                get_soundset_list),
+  GEN_STR_OPTION(default_sound_plugin_name, N_("Default sound plugin"),
+                get_soundplugin_list),
 
   GEN_BOOL_OPTION(solid_color_behind_units, N_("Solid unit background color")),
   GEN_BOOL_OPTION(sound_bell_at_new_turn,   N_("Sound bell at new turn")),
Index: client/options.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/options.h,v
retrieving revision 1.18
diff -u -r1.18 options.h
--- client/options.h    2002/07/23 02:48:45     1.18
+++ client/options.h    2002/08/01 22:20:12
@@ -59,6 +59,10 @@
   char *p_string_value;
   size_t string_length;
 
+  /* A function to return a static NULL-terminated list of possible
+     string values, or NULL for none. */
+  const char **(*p_string_vals)(void);
+
   /* volatile */
   void *p_gui_data;
 } client_option;
Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.81
diff -u -r1.81 tilespec.c
--- client/tilespec.c   2002/07/27 14:14:23     1.81
+++ client/tilespec.c   2002/08/01 22:20:13
@@ -50,6 +50,8 @@
 
 #include "tilespec.h"
 
+#define TILESPEC_SUFFIX ".tilespec"
+
 char *main_intro_filename;
 char *minimap_intro_filename;
 
@@ -115,6 +117,24 @@
 static bool no_backdrop = FALSE;
 
 /**********************************************************************
+  Returns a static list of tilesets available on the system by
+  searching all data directories for files matching TILESPEC_SUFFIX.
+  The list is NULL-terminated.
+***********************************************************************/
+const char **get_tileset_list(void)
+{
+  static const char **tileset_list = NULL;
+
+  if (!tileset_list) {
+    /* Note: this means you must restart the client after installing a new
+       tileset. */
+    tileset_list = datafilelist(TILESPEC_SUFFIX);
+  }
+
+  return tileset_list;
+}
+
+/**********************************************************************
   Gets full filename for tilespec file, based on input name.
   Returned data is allocated, and freed by user as required.
   Input name may be null, in which case uses default.
@@ -135,8 +155,8 @@
   if (tileset_name[0] == '\0') {
     tileset_name = tileset_default;
   }
-  fname = fc_malloc(strlen(tileset_name)+16);
-  sprintf(fname, "%s.tilespec", tileset_name);
+  fname = fc_malloc(strlen(tileset_name) + strlen(TILESPEC_SUFFIX) + 1);
+  sprintf(fname, "%s" TILESPEC_SUFFIX, tileset_name);
   
   dname = datafilename(fname);
   if (dname) {
Index: client/tilespec.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v
retrieving revision 1.28
diff -u -r1.28 tilespec.h
--- client/tilespec.h   2002/04/06 11:42:57     1.28
+++ client/tilespec.h   2002/08/01 22:20:13
@@ -26,6 +26,8 @@
 struct unit;
 struct player;
 
+const char **get_tileset_list(void);
+
 void tilespec_read_toplevel(const char *tileset_name);
 void tilespec_load_tiles(void);
 
Index: client/gui-xaw/optiondlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-xaw/optiondlg.c,v
retrieving revision 1.19
diff -u -r1.19 optiondlg.c
--- client/gui-xaw/optiondlg.c  2002/07/23 02:47:38     1.19
+++ client/gui-xaw/optiondlg.c  2002/08/01 22:20:13
@@ -22,9 +22,12 @@
 #include <X11/StringDefs.h>
 #include <X11/Xaw/Form.h>
 #include <X11/Xaw/Label.h>
+#include <X11/Xaw/List.h>
 #include <X11/Xaw/Command.h>
+#include <X11/Xaw/MenuButton.h>
 #include <X11/Xaw/SimpleMenu.h>
 #include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/SmeBSB.h>
 #include <X11/Xaw/AsciiText.h>  
 #include <X11/Xaw/Toggle.h>     
 
@@ -77,7 +80,8 @@
       XtVaSetValues((Widget) o->p_gui_data, XtNstring, valstr, NULL);
       break;
     case COT_STR:
-      XtVaSetValues((Widget) o->p_gui_data, XtNstring,
+      XtVaSetValues((Widget) o->p_gui_data,
+                   o->p_string_vals ? "label" : XtNstring,
                    o->p_string_value, NULL);
       break;
     }
@@ -90,6 +94,18 @@
 
 
 
+/****************************************************************
+  Callback to change the entry for a string option that has
+  a fixed list of possible entries.
+*****************************************************************/
+static void stropt_change_callback(Widget w,
+                                  XtPointer client_data,
+                                  XtPointer call_data)
+{
+  char* val = (char*)client_data;
+
+  XtVaSetValues(XtParent(XtParent(w)), "label", val, NULL);
+}
 
 /****************************************************************
 ...
@@ -151,8 +167,49 @@
                                NULL);
       XtAddCallback(prev_widget, XtNcallback, toggle_callback, NULL);
       break;
-    case COT_INT:
     case COT_STR:
+      if (o->p_string_vals) {
+       int i;
+       const char **vals = (*o->p_string_vals)();
+       Widget popupmenu;
+
+       prev_widget = XtVaCreateManagedWidget(o->name,
+                                             menuButtonWidgetClass,
+                                             option_form,
+                                             XtNfromHoriz, longest_label,
+                                             XtNfromVert, o->p_gui_data,
+                                             NULL);
+
+       popupmenu = XtVaCreatePopupShell("menu",
+                                        simpleMenuWidgetClass,
+                                        prev_widget,
+                                        NULL);
+
+       for (i = 0; vals[i]; i++) {
+         Widget entry = XtVaCreateManagedWidget(vals[i], smeBSBObjectClass,
+                                                popupmenu, NULL);
+         XtAddCallback(entry, XtNcallback, stropt_change_callback,
+                       (XtPointer)(vals[i]));
+       }
+
+       if (i == 0) {
+          /* We could disable this if there was just one possible choice,
+             too, but for values that are uninitialized (empty) this
+             would be confusing. */
+         XtSetSensitive(prev_widget, FALSE);
+       }
+
+#if 0
+       /* Setting the lable before the widget is drawn causes it to be
+          sized appropriately; if the value is empty this can be a
+          problem. */
+       XtVaSetValues(prev_widget, "label", o->p_string_value, NULL);
+#endif
+
+       break;
+      }
+      /* else fall through */
+    case COT_INT:
       prev_widget =
        XtVaCreateManagedWidget("input", asciiTextWidgetClass, option_form,
                                XtNfromHoriz, longest_label,
@@ -219,7 +276,9 @@
       sscanf(dp, "%d", o->p_int_value);
       break;
     case COT_STR:
-      XtVaGetValues(o->p_gui_data, XtNstring, &dp, NULL);
+      XtVaGetValues(o->p_gui_data,
+                   o->p_string_vals ? "label" : XtNstring,
+                   &dp, NULL);
       mystrlcpy(o->p_string_value,dp,o->string_length);
       break;
     }
Index: common/shared.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/shared.c,v
retrieving revision 1.86
diff -u -r1.86 shared.c
--- common/shared.c     2002/06/12 07:24:47     1.86
+++ common/shared.c     2002/08/01 22:20:14
@@ -15,6 +15,7 @@
 #endif
 
 #include <assert.h>
+#include <glob.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -711,96 +712,221 @@
 }
 
 /***************************************************************************
-  Returns a filename to access the specified file from a data directory;
-  The file may be in any of the directories in the data path, which is
-  specified internally or may be set as the environment variable
-  $FREECIV_PATH.  (A separated list of directories, where the separator
-  itself can specified internally.)
-  '~' at the start of a component (provided followed by '/' or '\0')
-  is expanded as $HOME.
+  Returns a list of data directory paths, in the order in which they should
+  be searched.  These paths are specified internally or may be set as the
+  environment variable $FREECIV_PATH (a separated list of directories,
+  where the separator itself is specified internally, platform-dependent).
+  '~' at the start of a component (provided followed by '/' or '\0') is
+  expanded as $HOME.
 
-  If the specified 'filename' is NULL, the returned string contains
-  the effective data path.  (But this should probably only be used
-  for debug output.)
-  
-  Returns NULL if the specified filename cannot be found in any of the
-  data directories.  (A file is considered "found" if it can be read-opened.)
-  The returned pointer points to static memory, so this function can
-  only supply one filename at a time.
+  The returned value is a static NULL-terminated list of strings.
+
+  num_dirs, if not NULL, will be set to the number of entries in the list.
 ***************************************************************************/
-char *datafilename(const char *filename)
+static char **get_data_dirs(int *num_dirs)
 {
-  static char *path = NULL;
-  static int num_dirs = 0;
+  char *path, *path2, *tok;
+  static int num = 0;
   static char **dirs = NULL;
-  static struct astring realfile = ASTRING_INIT;
-  int i;
 
-  if (!path) {
-    char *tok;
-    char *path2;
-
-    path = getenv("FREECIV_PATH");
-    if (!path) {
-      path = DEFAULT_DATA_PATH;
-    } else if (*path == '\0') {
-      freelog(LOG_ERROR, _("FREECIV_PATH is set but empty; "
-                          "using default path instead."));
-      path = DEFAULT_DATA_PATH;
+  /* The first time this function is called it will search and allocate
+   * the directory listing.  Subsequently we will already know the list
+   * and can just return it. */
+  if (dirs) {
+    if (num_dirs) {
+      *num_dirs = num;
     }
-    assert(path != NULL);
+    return dirs;
+  }
 
-    path2 = mystrdup(path);    /* something we can strtok */
+  path = getenv("FREECIV_PATH");
+  if (!path) {
+    path = DEFAULT_DATA_PATH;
+  } else if (*path == '\0') {
+    freelog(LOG_ERROR, _("FREECIV_PATH is set but empty; "
+                        "using default path instead."));
+    path = DEFAULT_DATA_PATH;
+  }
+  assert(path != NULL);
+  
+  path2 = mystrdup(path);      /* something we can strtok */
     
-    tok = strtok(path2, PATH_SEPARATOR);
-    do {
-      int i;                   /* strlen(tok), or -1 as flag */
-      
-      tok = skip_leading_spaces(tok);
-      remove_trailing_spaces(tok);
-      if (strcmp(tok, "/")!=0) {
-       remove_trailing_char(tok, '/');
-      }
+  tok = strtok(path2, PATH_SEPARATOR);
+  do {
+    int i;                     /* strlen(tok), or -1 as flag */
+
+    tok = skip_leading_spaces(tok);
+    remove_trailing_spaces(tok);
+    if (strcmp(tok, "/")!=0) {
+      remove_trailing_char(tok, '/');
+    }
       
-      i = strlen(tok);
-      if (tok[0] == '~') {
-       if (i>1 && tok[1] != '/') {
-         freelog(LOG_ERROR, "For \"%s\" in data path cannot expand '~'"
-                             " except as '~/'; ignoring", tok);
-         i = 0;   /* skip this one */
+    i = strlen(tok);
+    if (tok[0] == '~') {
+      if (i>1 && tok[1] != '/') {
+       freelog(LOG_ERROR, "For \"%s\" in data path cannot expand '~'"
+               " except as '~/'; ignoring", tok);
+       i = 0;   /* skip this one */
+      } else {
+       char *home = user_home_dir();
+       if (!home) {
+         freelog(LOG_VERBOSE,
+                 "No HOME, skipping data path component %s", tok);
+         i = 0;
        } else {
-         char *home = user_home_dir();
-         if (!home) {
-           freelog(LOG_VERBOSE,
-                   "No HOME, skipping data path component %s", tok);
-           i = 0;
-         } else {
-           int len = strlen(home) + i;    /* +1 -1 */
-           char *tmp = fc_malloc(len);
-           my_snprintf(tmp, len, "%s%s", home, tok+1);
-           tok = tmp;
-           i = -1;             /* flag to free tok below */
-         }
+         int len = strlen(home) + i;      /* +1 -1 */
+         char *tmp = fc_malloc(len);
+         my_snprintf(tmp, len, "%s%s", home, tok + 1);
+         tok = tmp;
+         i = -1;               /* flag to free tok below */
        }
       }
-      if (i != 0) {
-       /* We could check whether the directory exists and
-        * is readable etc?  Don't currently. */
-       num_dirs++;
-       dirs = fc_realloc(dirs, num_dirs*sizeof(char*));
-       dirs[num_dirs-1] = mystrdup(tok);
-       freelog(LOG_VERBOSE, "Data path component (%d): %s", num_dirs-1, tok);
-       if (i == -1) {
-         free(tok);
-         tok = NULL;
-       }
+    }
+    if (i != 0) {
+      /* We could check whether the directory exists and
+       * is readable etc?  Don't currently. */
+      num++;
+      dirs = fc_realloc(dirs, num * sizeof(char*));
+      dirs[num - 1] = mystrdup(tok);
+      freelog(LOG_VERBOSE, "Data path component (%d): %s", num - 1, tok);
+      if (i == -1) {
+       free(tok);
+       tok = NULL;
+      }
+    }
+
+    tok = strtok(NULL, PATH_SEPARATOR);
+  } while(tok);
+
+  /* NULL-terminate the list. */
+  dirs = fc_realloc(dirs, (num + 1) * sizeof(char*));
+  dirs[num] = NULL;
+
+  free(path2);
+  
+  if (num_dirs) {
+    *num_dirs = num;
+  }
+  return dirs;
+}
+
+/***************************************************************************
+  Returns a NULL-terminated list of filenames in the data directories
+  matching the given suffix.
+
+  The list is allocated when the function is called; it should either be
+  stored permanently or de-allocated (by free'ing each element and the
+  whole list).
+
+  The list is unsorted and may include duplicates (for now).  The suffixes
+  are removed from the filenames before the list is returned.
+***************************************************************************/
+const char **datafilelist(const char* suffix)
+{
+  char **dirs = get_data_dirs(NULL);
+  char **file_list = NULL;
+  int num_matches = 0;
+  int dir_num, i, j;
+
+  /* First assemble a full list of names. */
+  for (dir_num = 0; dirs[dir_num]; dir_num++) {
+    int num_entries;
+    int len = strlen(dirs[dir_num]) + strlen("/*") + strlen(suffix) + 1;
+    char* pattern = fc_malloc(len);
+    glob_t glob_result;
+
+    my_snprintf(pattern, len, "%s/*%s", dirs[dir_num], suffix);
+
+    glob(pattern, GLOB_MARK, NULL, &glob_result);
+
+    num_entries = glob_result.gl_pathc;
+
+    if (num_entries <= 0) {
+      continue;
+    }
+
+    /* Add the entries found to the list.  Note that we don't check
+       for duplicates. */
+    file_list = fc_realloc(file_list, (num_matches + num_entries) * 
+                          sizeof(*file_list));
+    for (i = 0; i < num_entries; i++) {
+      char* match = glob_result.gl_pathv[i];
+      int mlen = strlen(match);
+      char* next;
+
+      /* GLOB_MARK means a directory entry will get a trailing '/'.  We
+        only check for regular files, so we omit directories. */
+      if (match[mlen - 1] == '/') {
+       continue;
+      }
+
+      /* Clip the suffix. */
+      match[mlen - strlen(suffix)] = '\0';
+
+      /* Skip past leading directories. */
+      while ( (next = strchr(match, '/')) ) {
+       match = next + 1;
       }
+
+      /* Make sure the entry isn't empty (i.e. "~/.freeciv/.tilespec"). */
+      if (match[0] == '\0') {
+       continue;
+      }
+
+      file_list[num_matches++] = mystrdup(match);
+    }
+
+    globfree(&glob_result);
+    free(pattern);
+  }
+
+  /* Sort the list. */
+  qsort(file_list, num_matches, sizeof(*file_list), compare_strings_ptrs);
+
+  /* Remove duplicates (easy since it's sorted). */
+  i = j = 0;
+  while (j < num_matches) {
+    char *this = file_list[j];
+
+    for (j++; j < num_matches && !strcmp(this, file_list[j]); j++) {
+      free(file_list[j]);
+    }
 
-      tok = strtok(NULL, PATH_SEPARATOR);
-    } while(tok);
+    file_list[i] = this;
 
-    free(path2);
+    i++;
   }
+  num_matches = i;
+
+  /* NULL-terminate the whole thing. */
+  file_list = fc_realloc(file_list,
+                        (num_matches + 1) * sizeof(*file_list));
+  file_list[num_matches] = NULL;
+
+  return (const char **)file_list;
+}
+
+/***************************************************************************
+  Returns a filename to access the specified file from a data directory by
+  searching all data directories (as specified by get_data_dirs) for the
+  file.
+
+  If the specified 'filename' is NULL, the returned string contains
+  the effective data path.  (But this should probably only be used
+  for debug output.)
+  
+  Returns NULL if the specified filename cannot be found in any of the
+  data directories.  (A file is considered "found" if it can be read-opened.)
+  The returned pointer points to static memory, so this function can
+  only supply one filename at a time.
+***************************************************************************/
+char *datafilename(const char *filename)
+{
+  char **dirs;
+  int num_dirs, i;
+  static struct astring realfile = ASTRING_INIT;
+
+  dirs = get_data_dirs(&num_dirs);
 
   if (!filename) {
     int len = 1;               /* in case num_dirs==0 */
@@ -812,7 +938,7 @@
     realfile.str[0] = '\0';
     for(i=0; i<num_dirs; i++) {
       (void) mystrlcat(realfile.str, dirs[i], len);
-      if(i != num_dirs-1) {
+      if (i<num_dirs) {
        (void) mystrlcat(realfile.str, PATH_SEPARATOR, len);
       }
     }
Index: common/shared.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/shared.h,v
retrieving revision 1.95
diff -u -r1.95 shared.h
--- common/shared.h     2002/05/25 17:44:08     1.95
+++ common/shared.h     2002/08/01 22:20:14
@@ -109,6 +109,7 @@
 
 char *user_home_dir(void);
 char *user_username(void);
+const char **datafilelist(const char *suffix);
 char *datafilename(const char *filename);
 char *datafilename_required(const char *filename);
 

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