From 33ea0692f872cc1e179dc8777905f802e6bb5b0a Mon Sep 17 00:00:00 2001
From: Pepeto <pepeto@gna.org>
Date: Sun, 21 Nov 2010 19:27:43 +0100
Subject: [PATCH 1/2] Low level utility to convert a string to an integer.

---
 client/client_main.c         |   12 +++++-----
 client/gui-sdl/optiondlg.c   |    2 +-
 client/gui-win32/diplodlg.c  |    2 +-
 client/gui-win32/optiondlg.c |    2 +-
 client/gui-xaw/diplodlg.c    |    2 +-
 client/gui-xaw/optiondlg.c   |    2 +-
 common/featured_text.c       |    8 +++---
 common/packets.c             |    5 +---
 server/civserver.c           |   15 ++++++-------
 server/savegame.c            |   25 +++++++++++----------
 server/savegame2.c           |   15 +++++++------
 server/stdinhand.c           |   47 ++++++++++-------------------------------
 utility/log.c                |    6 ++--
 utility/netintf.c            |    2 +-
 utility/registry.c           |    2 +-
 utility/shared.c             |   33 +++++++++++++++++++++++++++++
 utility/shared.h             |    2 +
 17 files changed, 96 insertions(+), 86 deletions(-)

diff --git a/client/client_main.c b/client/client_main.c
index 6455ecc..ac831ab 100644
--- a/client/client_main.c
+++ b/client/client_main.c
@@ -350,7 +350,7 @@ int client_main(int argc, char *argv[])
     } else if (is_option("--Fatal", argv[i])) {
       if (i + 1 >= argc || '-' == argv[i + 1][0]) {
         fatal_assertions = SIGABRT;
-      } else if (1 == sscanf(argv[i + 1], "%d", &fatal_assertions)) {
+      } else if (str_to_int(argv[i + 1], &fatal_assertions)) {
         i++;
       } else {
         fc_fprintf(stderr, _("Invalid signal number \"%s\".\n"),
@@ -374,11 +374,11 @@ int client_main(int argc, char *argv[])
       sz_strlcpy(sound_plugin_name, option);
       free(option);
     } else if ((option = get_option_malloc("--port",argv,&i,argc))) {
-      if (sscanf(option, "%d", &server_port) != 1) {
-	fc_fprintf(stderr,
-		   _("Invalid port \"%s\" specified with --port option.\n"),
-		   option);
-	fc_fprintf(stderr, _("Try using --help.\n"));
+      if (!str_to_int(option, &server_port)) {
+        fc_fprintf(stderr,
+                   _("Invalid port \"%s\" specified with --port option.\n"),
+                   option);
+        fc_fprintf(stderr, _("Try using --help.\n"));
         exit(EXIT_FAILURE);
       }
       free(option);
diff --git a/client/gui-sdl/optiondlg.c b/client/gui-sdl/optiondlg.c
index aadf786..cead4e8 100644
--- a/client/gui-sdl/optiondlg.c
+++ b/client/gui-sdl/optiondlg.c
@@ -696,7 +696,7 @@ static void option_widget_apply(struct option *poption)
       char *str = convert_to_chars(widget->string16->text);
       int value;
 
-      if (1 == sscanf(str, "%d", &value)) {
+      if (str_to_int(str, &value)) {
         (void) option_int_set(poption, value);
       }
       free(str);
diff --git a/client/gui-win32/diplodlg.c b/client/gui-win32/diplodlg.c
index d2461a4..c3b402a 100644
--- a/client/gui-win32/diplodlg.c
+++ b/client/gui-win32/diplodlg.c
@@ -257,7 +257,7 @@ static void handle_gold_entry(struct Diplomacy_dialog *pdialog,int plr)
 
     SetWindowText(edit, "");
     pgiver = plr ? pdialog->treaty.plr1 : pdialog->treaty.plr0;
-    if (sscanf(buf, "%d", &amount) == 1 && amount >= 0
+    if (str_to_int(buf, &amount) && 0 <= amount
 	&& amount <= pgiver->economic.gold) {
        dsend_packet_diplomacy_create_clause_req(&client.conn,
 					player_number(pdialog->treaty.plr1),
diff --git a/client/gui-win32/optiondlg.c b/client/gui-win32/optiondlg.c
index e0344e8..21e55b4 100644
--- a/client/gui-win32/optiondlg.c
+++ b/client/gui-win32/optiondlg.c
@@ -69,7 +69,7 @@ static LONG CALLBACK option_proc(HWND dlg,UINT message,
 	  break;
 	case COT_INTEGER:
 	  GetWindowText((HWND)(gui_data),dp,sizeof(dp));
-          if (1 == sscanf(dp, "%d", &val)) {
+          if (str_to_int(dp, &val)) {
             (void) option_int_set(poption, val);
           }
 	  break;
diff --git a/client/gui-xaw/diplodlg.c b/client/gui-xaw/diplodlg.c
index 1bf49ee..4b79434 100644
--- a/client/gui-xaw/diplodlg.c
+++ b/client/gui-xaw/diplodlg.c
@@ -964,7 +964,7 @@ void diplodlg_key_gold(Widget w)
     int amount;
     
     XtVaGetValues(w, XtNstring, &dp, NULL);
-    if (sscanf(dp, "%d", &amount) == 1 && amount >= 0
+    if (str_to_int(dp, &amount) && 0 <= amount
 	&& amount <= pgiver->economic.gold) {
       dsend_packet_diplomacy_create_clause_req(&client.conn,
 					       player_number(pdialog->treaty.plr1),
diff --git a/client/gui-xaw/optiondlg.c b/client/gui-xaw/optiondlg.c
index 15ccf2e..0773ab1 100644
--- a/client/gui-xaw/optiondlg.c
+++ b/client/gui-xaw/optiondlg.c
@@ -402,7 +402,7 @@ static void option_dialog_option_apply(struct option *poption)
         int val;
 
         XtVaGetValues(widget, XtNstring, &dp, NULL);
-        if (1 == sscanf(dp, "%d", &val)) {
+        if (str_to_int(dp, &val)) {
           option_int_set(poption, val);
         }
       }
diff --git a/common/featured_text.c b/common/featured_text.c
index e53a986..aeaa9f8 100644
--- a/common/featured_text.c
+++ b/common/featured_text.c
@@ -277,7 +277,7 @@ static bool text_tag_init_from_sequence(struct text_tag *ptag,
                               "city link without id.");
             return FALSE;
           }
-          if (1 != sscanf(buf, "%d", &ptag->link.id)) {
+          if (!str_to_int(buf, &ptag->link.id)) {
             log_featured_text("text_tag_init_from_sequence(): "
                               "city link without valid id (\"%s\").", buf);
             return FALSE;
@@ -301,7 +301,7 @@ static bool text_tag_init_from_sequence(struct text_tag *ptag,
                               "tile link without x coordinate.");
             return FALSE;
           }
-          if (1 != sscanf(buf, "%d", &x)) {
+          if (!str_to_int(buf, &x)) {
             log_featured_text("text_tag_init_from_sequence(): "
                               "tile link without valid x coordinate "
                               "(\"%s\").", buf);
@@ -313,7 +313,7 @@ static bool text_tag_init_from_sequence(struct text_tag *ptag,
                               "tile link without y coordinate.");
             return FALSE;
           }
-          if (1 != sscanf(buf, "%d", &y)) {
+          if (!str_to_int(buf, &y)) {
             log_featured_text("text_tag_init_from_sequence(): "
                               "tile link without valid y coordinate "
                               "(\"%s\").", buf);
@@ -339,7 +339,7 @@ static bool text_tag_init_from_sequence(struct text_tag *ptag,
                               "unit link without id.");
             return FALSE;
           }
-          if (1 != sscanf(buf, "%d", &ptag->link.id)) {
+          if (!str_to_int(buf, &ptag->link.id)) {
             log_featured_text("text_tag_init_from_sequence(): "
                               "unit link without valid id (\"%s\").", buf);
             return FALSE;
diff --git a/common/packets.c b/common/packets.c
index b57ab5b..ed80703 100644
--- a/common/packets.c
+++ b/common/packets.c
@@ -96,10 +96,7 @@ static inline int get_compression_level(void)
   if (-2 == level) {
     const char *s = getenv("FREECIV_COMPRESSION_LEVEL");
 
-    if (NULL == s
-        || 1 != sscanf(s, "%d", &level)
-        || -1 > level
-        || 9 < level) {
+    if (NULL == s || !str_to_int(s, &level) || -1 > level || 9 < level) {
       level = -1;
     }
   }
diff --git a/server/civserver.c b/server/civserver.c
index b7e74e2..41ed94a 100644
--- a/server/civserver.c
+++ b/server/civserver.c
@@ -209,8 +209,7 @@ int main(int argc, char *argv[])
     } else if (is_option("--Fatal", argv[inx])) {
       if (inx + 1 >= argc || '-' == argv[inx + 1][0]) {
         srvarg.fatal_assertions = SIGABRT;
-      } else if (1 == sscanf(argv[inx + 1],
-                             "%d", &srvarg.fatal_assertions)) {
+      } else if (str_to_int(argv[inx + 1], &srvarg.fatal_assertions)) {
         inx++;
       } else {
         fc_fprintf(stderr, _("Invalid signal number \"%s\".\n"),
@@ -237,9 +236,9 @@ int main(int argc, char *argv[])
       sz_strlcpy(srvarg.metaserver_name, option);
       free(option);
     } else if ((option = get_option_malloc("--port", argv, &inx, argc))) {
-      if (sscanf(option, "%d", &srvarg.port) != 1) {
-	showhelp = TRUE;
-	break;
+      if (!str_to_int(option, &srvarg.port)) {
+        showhelp = TRUE;
+        break;
       }
       free(option);
     } else if ((option = get_option_malloc("--bind", argv, &inx, argc))) {
@@ -247,9 +246,9 @@ int main(int argc, char *argv[])
     } else if ((option = get_option_malloc("--read", argv, &inx, argc)))
       srvarg.script_filename = option; /* Never freed. */
     else if ((option = get_option_malloc("--quitidle", argv, &inx, argc))) {
-      if (sscanf(option, "%d", &srvarg.quitidle) != 1) {
-	showhelp = TRUE;
-	break;
+      if (!str_to_int(option, &srvarg.quitidle)) {
+        showhelp = TRUE;
+        break;
       }
       free(option);
     } else if (is_option("--exit-on-end", argv[inx])) {
diff --git a/server/savegame.c b/server/savegame.c
index bf4e841..0910037 100644
--- a/server/savegame.c
+++ b/server/savegame.c
@@ -623,20 +623,21 @@ static char *quote_block(const void *const data, int length)
   return buffer;
 }
 
-/***************************************************************
-Unquote a string. The unquoted data is written into dest. If the
-unqoted data will be largern than dest_length the function aborts. It
-returns the actual length of the unquoted block.
-***************************************************************/
+/****************************************************************************
+  Unquote a string. The unquoted data is written into dest. If the unquoted
+  data will be largern than dest_length the function aborts. It returns the
+  actual length of the unquoted block.
+****************************************************************************/
 static int unquote_block(const char *const quoted_, void *dest,
-			 int dest_length)
+                         int dest_length)
 {
-  int i, length, parsed, tmp;
+  int i, length, tmp;
   char *endptr;
   const char *quoted = quoted_;
+  bool parsed;
 
-  parsed = sscanf(quoted, "%d", &length);
-  fc_assert_ret_val(parsed == 1, 0);
+  parsed = str_to_int(quoted, &length);
+  fc_assert_ret_val(TRUE == parsed, 0);
 
   fc_assert_ret_val(length <= dest_length, 0);
   quoted = strchr(quoted, ':');
@@ -1251,7 +1252,7 @@ static void map_load_owner(struct section_file *file,
         if (strcmp(token1, "-") == 0) {
           owner = NULL;
         } else {
-          fc_assert_exit_msg(1 == sscanf(token1, "%d", &number),
+          fc_assert_exit_msg(str_to_int(token1, &number),
                              "Savegame corrupt - got map owner %s "
                              "in (%d, %d).", token1, x, y);
           owner = player_by_number(number);
@@ -1259,7 +1260,7 @@ static void map_load_owner(struct section_file *file,
         if (strcmp(token2, "-") == 0) {
           claimer = NULL;
         } else {
-          fc_assert_exit_msg(1 == sscanf(token2, "%d", &number),
+          fc_assert_exit_msg(str_to_int(token2, &number),
                              "Savegame corrupt - got map source %s "
                              "in (%d, %d).", token2, x, y);
           claimer = index_to_tile(number);
@@ -2797,7 +2798,7 @@ static int *player_load_cities_worked_map(struct section_file *file,
       if (strcmp(token, "-") == 0) {
         number = -1;
       } else {
-        fc_assert_exit_msg(1 == sscanf(token, "%d", &number) && 0 < number,
+        fc_assert_exit_msg(str_to_int(token, &number) && 0 < number,
                            "Savegame corrupt - got tile worked by city "
                            "id=%s in (%d, %d).", token, x, y);
       }
diff --git a/server/savegame2.c b/server/savegame2.c
index 225a8dd..04027ba 100644
--- a/server/savegame2.c
+++ b/server/savegame2.c
@@ -981,12 +981,13 @@ static char *quote_block(const void *const data, int length)
 static int unquote_block(const char *const quoted_, void *dest,
                          int dest_length)
 {
-  int i, length, parsed, tmp;
+  int i, length, tmp;
   char *endptr;
   const char *quoted = quoted_;
+  bool parsed;
 
-  parsed = sscanf(quoted, "%d", &length);
-  fc_assert_ret_val(parsed == 1, 0);
+  parsed = str_to_int(quoted, &length);
+  fc_assert_ret_val(TRUE == parsed, 0);
 
   fc_assert_ret_val(length <= dest_length, 0);
   quoted = strchr(quoted, ':');
@@ -2509,7 +2510,7 @@ static void sg_load_map_owner(struct loaddata *loading)
       if (strcmp(token1, "-") == 0) {
         owner = NULL;
       } else {
-        sg_failure_ret(sscanf(token1, "%d", &number),
+        sg_failure_ret(str_to_int(token1, &number),
                        "Got map owner %s in (%d, %d).", token1, x, y);
         owner = player_by_number(number);
       }
@@ -2520,7 +2521,7 @@ static void sg_load_map_owner(struct loaddata *loading)
       if (strcmp(token2, "-") == 0) {
         claimer = NULL;
       } else {
-        sg_failure_ret(sscanf(token2, "%d", &number),
+        sg_failure_ret(str_to_int(token2, &number),
                        "Got map source %s in (%d, %d).", token2, x, y);
         claimer = index_to_tile(number);
       }
@@ -2622,7 +2623,7 @@ static void sg_load_map_worked(struct loaddata *loading)
       if (strcmp(token, "-") == 0) {
         number = -1;
       } else {
-        sg_failure_ret(1 == sscanf(token, "%d", &number) && 0 < number,
+        sg_failure_ret(str_to_int(token, &number) && 0 < number,
                        "Savegame corrupt - got tile worked by city "
                        "id=%s in (%d, %d).", token, x, y);
       }
@@ -4803,7 +4804,7 @@ static void sg_load_player_vision(struct loaddata *loading,
           continue;
         }
 
-        sg_failure_ret(1 == sscanf(token, "%d", &number),
+        sg_failure_ret(str_to_int(token, &number),
                        "Savegame corrupt - got tile owner=%s in (%d, %d).",
                        token, x, y);
         map_get_player_tile(ptile, plr)->owner = player_by_number(number);
diff --git a/server/stdinhand.c b/server/stdinhand.c
index 8ee19b5..fce2426 100644
--- a/server/stdinhand.c
+++ b/server/stdinhand.c
@@ -1574,9 +1574,9 @@ static bool timeout_command(struct connection *caller, char *str, bool check)
   ntokens = get_tokens(buf, arg, 4, TOKEN_DELIMITERS);
 
   for (i = 0; i < ntokens; i++) {
-    if (sscanf(arg[i], "%d", timeouts[i]) != 1) {
+    if (!str_to_int(arg[i], timeouts[i])) {
       cmd_reply(CMD_TIMEOUT, caller, C_FAIL, _("Invalid argument %d."),
-		i + 1);
+                i + 1);
     }
     free(arg[i]);
   }
@@ -2210,7 +2210,7 @@ static bool team_command(struct connection *caller, char *str, bool check)
   if (NULL == tslot) {
     int teamno;
 
-    if (sscanf(arg[1], "%d", &teamno) == 1) {
+    if (str_to_int(arg[1], &teamno)) {
       tslot = team_slot_by_number(teamno);
     }
   }
@@ -2353,7 +2353,7 @@ static bool vote_command(struct connection *caller, char *str,
       goto CLEANUP;
     }
   } else {
-    if (sscanf(arg[1], "%d", &which) <= 0) {
+    if (!str_to_int(arg[1], &which)) {
       cmd_reply(CMD_VOTE, caller, C_SYNTAX, _("Value must be an integer."));
       goto CLEANUP;
     }
@@ -2445,7 +2445,7 @@ static bool cancelvote_command(struct connection *caller,
                 _("You are not allowed to use this command."));
       return FALSE;
     }
-  } else if (sscanf(arg, "%d", &vote_no) == 1) {
+  } else if (str_to_int(arg, &vote_no)) {
     /* Cancel one particular vote (needs some privileges if the vote
      * is not owned). */
     if (!(pvote = get_vote_by_no(vote_no))) {
@@ -2594,7 +2594,7 @@ static bool debug_command(struct connection *caller, char *str,
                 command_synopsis(command_by_number(CMD_DEBUG)));
       goto cleanup;
     }
-    if (sscanf(arg[1], "%d", &x) != 1 || sscanf(arg[2], "%d", &y) != 1) {
+    if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
       cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
       goto cleanup;
     }
@@ -2625,7 +2625,7 @@ static bool debug_command(struct connection *caller, char *str,
                 command_synopsis(command_by_number(CMD_DEBUG)));
       goto cleanup;
     }
-    if (sscanf(arg[1], "%d", &x) != 1 || sscanf(arg[2], "%d", &y) != 1) {
+    if (!str_to_int(arg[1], &x) || !str_to_int(arg[2], &y)) {
       cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 & 3 must be integer."));
       goto cleanup;
     }
@@ -2667,7 +2667,7 @@ static bool debug_command(struct connection *caller, char *str,
               command_synopsis(command_by_number(CMD_DEBUG)));
       goto cleanup;
     }
-    if (sscanf(arg[1], "%d", &id) != 1) {
+    if (!str_to_int(arg[1], &id)) {
       cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 2 must be integer."));
       goto cleanup;
     }
@@ -2698,21 +2698,6 @@ static bool debug_command(struct connection *caller, char *str,
   return TRUE;
 }
 
-/****************************************************************************
-  Returns TRUE if the string contains only digits.
-****************************************************************************/
-static inline bool string_contains_only_digits(const char *str)
-{
-  if ('-' == *str || '+' == *str) {
-    /* Ignore the sign. */
-    str++;
-  }
-  while (fc_isdigit(*str)) {
-    str++;
-  }
-  return ('\0' == *str);
-}
-
 /******************************************************************
   ...
 ******************************************************************/
@@ -2776,12 +2761,7 @@ static bool set_command(struct connection *caller, char *str, bool check)
     break;
 
   case SSET_INT:
-    if (sscanf(args[1], "%d", &val) != 1) {
-      cmd_reply(CMD_SET, caller, C_SYNTAX, _("Value must be an integer."));
-      goto cleanup;
-    }
-    /* Make sure the input string only contains digits. */
-    if (!string_contains_only_digits(args[1])) {
+    if (!str_to_int(args[1], &val)) {
       cmd_reply(CMD_SET, caller, C_SYNTAX,
                 _("The parameter %s should only contain +- and 0-9."),
                 setting_name(pset));
@@ -3750,8 +3730,7 @@ static bool unignore_command(struct connection *caller,
     *c++ = '\0';
     if ('\0' == buf[0]) {
       first = 1;
-    } else if (1 != sscanf(buf, "%d", &first)
-               || !string_contains_only_digits(buf)) {
+    } else if (!str_to_int(buf, &first)) {
       *--c = '-';
       cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
                 _("\"%s\" is not a valid range. Try /help unignore."), buf);
@@ -3759,16 +3738,14 @@ static bool unignore_command(struct connection *caller,
     }
     if ('\0' == *c) {
       last = n;
-    } else if (1 != sscanf(c, "%d", &last)
-               || !string_contains_only_digits(c)) {
+    } else if (!str_to_int(c, &last)) {
       *--c = '-';
       cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
                 _("\"%s\" is not a valid range. Try /help unignore."), buf);
       return FALSE;
     }
   } else {
-    if (1 != sscanf(buf, "%d", &first)
-        || !string_contains_only_digits(buf)) {
+    if (!str_to_int(buf, &first)) {
       cmd_reply(CMD_UNIGNORE, caller, C_SYNTAX,
                 _("\"%s\" is not a valid range. Try /help unignore."), buf);
       return FALSE;
diff --git a/utility/log.c b/utility/log.c
index f8228f7..217b6aa 100644
--- a/utility/log.c
+++ b/utility/log.c
@@ -86,7 +86,7 @@ bool log_parse_level_str(const char *level_str, enum log_level *ret_level)
   }
   if (n == 0) {
     /* Global log level. */
-    if (sscanf(level_str, "%d", FC_ENUM_PTR(level)) != 1) {
+    if (!str_to_int(level_str, FC_ENUM_PTR(level))) {
       fc_fprintf(stderr, _("Bad log level \"%s\".\n"), level_str);
       return FALSE;
     }
@@ -150,12 +150,12 @@ bool log_parse_level_str(const char *level_str, enum log_level *ret_level)
       d = strchr(d + 1, ',');
       if (d && *pc != '\0' && d[1] != '\0') {
         d[0] = '\0';
-        if (sscanf(pc, "%d", &pfile->min) != 1) {
+        if (!str_to_int(pc, &pfile->min)) {
           fc_fprintf(stderr, _("Not an integer: '%s'\n"), pc);
           ret = FALSE;
           goto out;
         }
-        if (sscanf(d + 1, "%d", &pfile->max) != 1) {
+        if (!str_to_int(d + 1, &pfile->max)) {
           fc_fprintf(stderr, _("Not an integer: '%s'\n"), d + 1);
           ret = FALSE;
           goto out;
diff --git a/utility/netintf.c b/utility/netintf.c
index 0af3184..6942cd5 100644
--- a/utility/netintf.c
+++ b/utility/netintf.c
@@ -499,7 +499,7 @@ const char *fc_lookup_httpd(char *server, int *port, const char *url)
   }
 
   /* snarf port. */
-  if (!pport || sscanf(pport+1, "%d", port) != 1) {
+  if (NULL == pport || !str_to_int(pport + 1, port)) {
     *port = 80;
   }
 
diff --git a/utility/registry.c b/utility/registry.c
index d6af1d9..f471dfd 100644
--- a/utility/registry.c
+++ b/utility/registry.c
@@ -3383,7 +3383,7 @@ static void entry_from_token(struct section *psection, const char *name,
   if (fc_isdigit(tok[0]) || ('-' == tok[0] && fc_isdigit(tok[1]))) {
     int value;
 
-    if (1 == sscanf(tok, "%d", &value)) {
+    if (str_to_int(tok, &value)) {
       (void) section_entry_int_new(psection, name, value);
       DEBUG_ENTRIES("entry %s %d", name, value);
       return;
diff --git a/utility/shared.c b/utility/shared.c
index 71f3a51..9fb5b6d 100644
--- a/utility/shared.c
+++ b/utility/shared.c
@@ -682,6 +682,39 @@ size_t loud_strlcpy(char *buffer, const char *str, size_t len,
   return fc_strlcpy(buffer, str, len);
 }
 
+/****************************************************************************
+  Convert 'str' to it's string reprentation if possible. 'pint' can be NULL,
+  then it will only test 'str' only contains a number.
+****************************************************************************/
+bool str_to_int(const char *str, int *pint)
+{
+  const char *start;
+
+  fc_assert_ret_val(NULL != str, FALSE);
+
+  while (fc_isspace(*str)) {
+    /* Skip leading spaces. */
+    str++;
+  }
+
+  start = str;
+  if ('-' == *str || '+' == *str) {
+    /* Handle sign. */
+    str++;
+  }
+  while (fc_isdigit(*str)) {
+    /* Digits. */
+    str++;
+  }
+
+  while (fc_isspace(*str)) {
+    /* Ignore trailing spaces. */
+    str++;
+  }
+
+  return ('\0' == *str && (NULL == pint || 1 == sscanf(start, "%d", pint)));
+}
+
 /***************************************************************************
   Returns string which gives users home dir, as specified by $HOME.
   Gets value once, and then caches result.
diff --git a/utility/shared.h b/utility/shared.h
index a5921db..0054e48 100644
--- a/utility/shared.h
+++ b/utility/shared.h
@@ -128,6 +128,8 @@ size_t loud_strlcpy(char *buffer, const char *str, size_t len,
 
 char *end_of_strn(char *str, int *nleft);
 
+bool str_to_int(const char *str, int *pint);
+
 /**************************************************************************
 ...
 **************************************************************************/
-- 
1.7.0.4

