foxygit / doom Log in
commit 62b5c602821cee8ac703ebe6362f3e1fc6d9ad3c
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Sat Mar 29 20:08:31 2014 -0400
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Sat Mar 29 20:08:31 2014 -0400

    misc: Add string utility functions.

    It's more readable to write "M_StringEndsWith(..." than doing a bunch of
    pointer arithmetic, and this is a common pattern. Also add
    M_StringStartsWith, M_StringJoin and M_StringCopy. The latter is a
    safe alternative for strcpy() that works the same as OpenBSD's
    strlcpy(). Use these functions in a few places where it makes sense.
---
 src/d_iwad.c         |   9 ++---
 src/doom/d_main.c    |  20 +++++-----
 src/i_pcsound.c      |   6 +--
 src/i_sdlsound.c     |  11 ++---
 src/m_misc.c         | 111 ++++++++++++++++++++++++++++++++++++++++++++-------
 src/m_misc.h         |   4 ++
 src/net_packet.c     |  10 +++--
 src/net_structrw.c   |   6 +--
 src/setup/mainmenu.c |   2 +-
 src/w_merge.c        |   3 +-
 10 files changed, 135 insertions(+), 47 deletions(-)

diff --git a/src/d_iwad.c b/src/d_iwad.c
index f4155349..293553a2 100644
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -423,7 +423,7 @@ static char *CheckDirectoryHasIWAD(char *dir, char *iwadname)

     // As a special case, the "directory" may refer directly to an
     // IWAD file if the path comes from DOOMWADDIR or DOOMWADPATH.
-
+
     if (DirIsFile(dir, iwadname) && M_FileExists(dir))
     {
         return strdup(dir);
@@ -432,15 +432,14 @@ static char *CheckDirectoryHasIWAD(char *dir, char *iwadname)
     // Construct the full path to the IWAD if it is located in
     // this directory, and check if it exists.

-    filename = malloc(strlen(dir) + strlen(iwadname) + 3);
-
     if (!strcmp(dir, "."))
     {
-        strcpy(filename, iwadname);
+        filename = strdup(iwadname);
     }
     else
     {
-        sprintf(filename, "%s%c%s", dir, DIR_SEPARATOR, iwadname);
+        char sep[] = {DIR_SEPARATOR, '\0'};
+        filename = M_StringJoin(dir, sep, iwadname);
     }

     if (M_FileExists(filename))
diff --git a/src/doom/d_main.c b/src/doom/d_main.c
index e6b993c4..fa84bc3b 100644
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -1352,19 +1352,19 @@ void D_DoomMain (void)

     if (p)
     {
-        if (!strcasecmp(myargv[p+1] + strlen(myargv[p+1]) - 4, ".lmp"))
+        if (M_StringEndsWith(myargv[p + 1], ".lmp"))
         {
-            strcpy(file, myargv[p + 1]);
+            M_StringCopy(file, myargv[p + 1], sizeof(file));
         }
         else
         {
-	    sprintf (file,"%s.lmp", myargv[p+1]);
+            snprintf(file, sizeof(file), "%s.lmp", myargv[p+1]);
         }

-	if (D_AddFile (file))
+        if (D_AddFile(file))
         {
-            strncpy(demolumpname, lumpinfo[numlumps - 1].name, 8);
-            demolumpname[8] = '\0';
+            M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+                         sizeof(demolumpname));

             printf("Playing demo %s.\n", file);
         }
@@ -1374,10 +1374,8 @@ void D_DoomMain (void)
             // the demo in the same way as Vanilla Doom.  This makes
             // tricks like "-playdemo demo1" possible.

-            strncpy(demolumpname, myargv[p + 1], 8);
-            demolumpname[8] = '\0';
+            M_StringCopy(demolumpname, myargv[p + 1], sizeof(demolumpname));
         }
-
     }

     I_AtExit((atexit_func_t) G_CheckDemoStatus, true);
@@ -1686,8 +1684,8 @@ void D_DoomMain (void)

     if (startloadgame >= 0)
     {
-        strcpy(file, P_SaveGameFile(startloadgame));
-	G_LoadGame (file);
+        M_StringCopy(file, P_SaveGameFile(startloadgame), sizeof(file));
+	G_LoadGame(file);
     }

     if (gameaction != ga_loadgame )
diff --git a/src/i_pcsound.c b/src/i_pcsound.c
index 655f5a8b..0303d2e4 100644
--- a/src/i_pcsound.c
+++ b/src/i_pcsound.c
@@ -30,7 +30,7 @@

 #include "deh_str.h"
 #include "i_sound.h"
-
+#include "m_misc.h"
 #include "w_wad.h"
 #include "z_zone.h"

@@ -253,11 +253,11 @@ static int I_PCS_GetSfxLumpNum(sfxinfo_t* sfx)

     if (use_sfx_prefix)
     {
-        sprintf(namebuf, "dp%s", DEH_String(sfx->name));
+        snprintf(namebuf, sizeof(namebuf), "dp%s", DEH_String(sfx->name));
     }
     else
     {
-        strcpy(namebuf, DEH_String(sfx->name));
+        M_StringCopy(namebuf, DEH_String(sfx->name), sizeof(namebuf));
     }

     return W_GetNumForName(namebuf);
diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c
index e60e18e3..b35c83ea 100644
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -43,6 +43,7 @@
 #include "i_system.h"
 #include "i_swap.h"
 #include "m_argv.h"
+#include "m_misc.h"
 #include "w_wad.h"
 #include "z_zone.h"

@@ -717,7 +718,7 @@ static boolean CacheSFX(sfxinfo_t *sfxinfo)
     return true;
 }

-static void GetSfxLumpName(sfxinfo_t *sfx, char *buf)
+static void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len)
 {
     // Linked sfx lumps? Get the lump number for the sound linked to.

@@ -731,11 +732,11 @@ static void GetSfxLumpName(sfxinfo_t *sfx, char *buf)

     if (use_sfx_prefix)
     {
-        sprintf(buf, "ds%s", DEH_String(sfx->name));
+        snprintf(buf, buf_len, "ds%s", DEH_String(sfx->name));
     }
     else
     {
-        strcpy(buf, DEH_String(sfx->name));
+        M_StringCopy(buf, DEH_String(sfx->name), buf_len);
     }
 }

@@ -765,7 +766,7 @@ static void I_SDL_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)
             fflush(stdout);
         }

-        GetSfxLumpName(&sounds[i], namebuf);
+        GetSfxLumpName(&sounds[i], namebuf, sizeof(buf));

         sounds[i].lumpnum = W_CheckNumForName(namebuf);

@@ -815,7 +816,7 @@ static int I_SDL_GetSfxLumpNum(sfxinfo_t *sfx)
 {
     char namebuf[9];

-    GetSfxLumpName(sfx, namebuf);
+    GetSfxLumpName(sfx, namebuf, sizeof(namebuf));

     return W_GetNumForName(namebuf);
 }
diff --git a/src/m_misc.c b/src/m_misc.c
index f3e11c36..7bab3f1b 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -298,55 +298,136 @@ char *M_StrCaseStr(char *haystack, char *needle)

 //
 // String replace function.
-// Returns a Z_Malloc()ed string.
 //

 char *M_StringReplace(char *haystack, char *needle, char *replacement)
 {
-    char *result, *p, *dst;
+    char *result, *p;
+    char *dst;
     size_t needle_len = strlen(needle);
-    int n;
+    size_t result_len, dst_len;

-    // Count number of occurrences of 'p':
+    // Iterate through occurrences of 'needle' and calculate the size of
+    // the new string.
+    result_len = strlen(haystack) + 1;
+    p = haystack;

-    for (p = haystack, n = 0;; ++n)
+    for (;;)
     {
         p = strstr(p, needle);
-
         if (p == NULL)
         {
             break;
         }

         p += needle_len;
+        result_len += strlen(replacement) - needle_len;
     }

     // Construct new string.

-    result = Z_Malloc(strlen(haystack)
-                      + (strlen(replacement) - needle_len) * n
-                      + 1,
-                      PU_STATIC, NULL);
+    result = malloc(result_len);
+    if (result == NULL)
+    {
+        I_Error("M_StringReplace: Failed to allocate new string");
+        return NULL;
+    }

-    dst = result;
+    dst = result; dst_len = result_len;
     p = haystack;

     while (*p != '\0')
     {
         if (!strncmp(p, needle, needle_len))
         {
-            strcpy(dst, replacement);
-            dst += strlen(replacement);
+            M_StringCopy(dst, replacement, dst_len);
             p += needle_len;
+            dst += strlen(replacement);
+            dst_len -= strlen(replacement);
         }
         else
         {
             *dst = *p;
-            ++dst;
+            ++dst; --dst_len;
             ++p;
         }
     }
-    *dst = '\0';
+
+    return result;
+}
+
+// Safe string copy function that works like OpenBSD's strlcpy().
+// Returns true if the string was not truncated.
+
+boolean M_StringCopy(char *dest, char *src, size_t dest_size)
+{
+    strncpy(dest, src, dest_size);
+    dest[dest_size - 1] = '\0';
+    return strlen(dest) == strlen(src);
+}
+
+// Returns true if 's' begins with the specified prefix.
+
+boolean M_StringStartsWith(char *s, char *prefix)
+{
+    return strlen(s) > strlen(prefix)
+        && strncmp(s, prefix, strlen(prefix)) == 0;
+}
+
+// Returns true if 's' ends with the specified suffix.
+
+boolean M_StringEndsWith(char *s, char *suffix)
+{
+    return strlen(s) >= strlen(suffix)
+        && strcmp(s + strlen(s) - strlen(suffix), suffix) == 0;
+}
+
+// Return a newly-malloced string with all the strings given as arguments
+// concatenated together.
+
+char *M_StringJoin(char *s, ...)
+{
+    char *result, *v;
+    va_list args;
+    size_t result_len;
+
+    result_len = strlen(s) + 1;
+
+    va_start(args, s);
+    for (;;)
+    {
+        v = va_arg(args, char *);
+        if (v == NULL)
+        {
+            break;
+        }
+
+        result_len += strlen(v);
+    }
+    va_end(args);
+
+    result = malloc(result_len);
+
+    if (result == NULL)
+    {
+        I_Error("M_StringJoin: Failed to allocate new string.");
+        return NULL;
+    }
+
+    M_StringCopy(result, s, result_len);
+
+    va_start(args, s);
+    for (;;)
+    {
+        v = va_arg(args, char *);
+        if (v == NULL)
+        {
+            break;
+        }
+
+        strncat(result, v, result_len);
+    }
+    va_end(args);

     return result;
 }
diff --git a/src/m_misc.h b/src/m_misc.h
index e96e2383..f81d6dc8 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -43,7 +43,11 @@ boolean M_StrToInt(const char *str, int *result);
 void M_ExtractFileBase(char *path, char *dest);
 void M_ForceUppercase(char *text);
 char *M_StrCaseStr(char *haystack, char *needle);
+boolean M_StringCopy(char *dest, char *src, size_t dest_size);
 char *M_StringReplace(char *haystack, char *needle, char *replacement);
+char *M_StringJoin(char *s, ...);
+boolean M_StringStartsWith(char *s, char *prefix);
+boolean M_StringEndsWith(char *s, char *suffix);
 char *M_OEMToUTF8(const char *ansi);

 #endif
diff --git a/src/net_packet.c b/src/net_packet.c
index 86809167..f984ca1e 100644
--- a/src/net_packet.c
+++ b/src/net_packet.c
@@ -24,6 +24,7 @@
 //-----------------------------------------------------------------------------

 #include <string.h>
+#include "m_misc.h"
 #include "net_packet.h"
 #include "z_zone.h"

@@ -280,19 +281,22 @@ void NET_WriteInt32(net_packet_t *packet, unsigned int i)
 void NET_WriteString(net_packet_t *packet, char *string)
 {
     byte *p;
+    size_t string_size;
+
+    string_size = strlen(string) + 1;

     // Increase the packet size until large enough to hold the string

-    while (packet->len + strlen(string) + 1 > packet->alloced)
+    while (packet->len + string_size > packet->alloced)
     {
         NET_IncreasePacket(packet);
     }

     p = packet->data + packet->len;

-    strcpy((char *) p, string);
+    M_StringCopy((char *) p, string, string_size);

-    packet->len += strlen(string) + 1;
+    packet->len += string_size;
 }


diff --git a/src/net_structrw.c b/src/net_structrw.c
index 10b035c0..fd5008de 100644
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -27,7 +27,7 @@
 #include <ctype.h>

 #include "doomtype.h"
-
+#include "m_misc.h"
 #include "net_packet.h"
 #include "net_structrw.h"

@@ -493,7 +493,7 @@ boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data)
             return false;
         }

-        strcpy(data->player_names[i], s);
+        M_StringCopy(data->player_names[i], s, MAXPLAYERNAME);

         s = NET_ReadString(packet);

@@ -502,7 +502,7 @@ boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data)
             return false;
         }

-        strcpy(data->player_addrs[i], s);
+        M_StringCopy(data->player_addrs[i], s, MAXPLAYERNAME);
     }

     return NET_ReadSHA1Sum(packet, data->wad_sha1sum)
diff --git a/src/setup/mainmenu.c b/src/setup/mainmenu.c
index 8e5ebdad..81688b29 100644
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@ -329,7 +329,7 @@ static void SetWindowTitle(void)

     TXT_SetDesktopTitle(title);

-    Z_Free(title);
+    free(title);
 }

 // Initialize the textscreen library.
diff --git a/src/w_merge.c b/src/w_merge.c
index 027c6c2c..e7ba224f 100644
--- a/src/w_merge.c
+++ b/src/w_merge.c
@@ -33,6 +33,7 @@

 #include "doomtype.h"
 #include "i_system.h"
+#include "m_misc.h"
 #include "w_merge.h"
 #include "w_wad.h"
 #include "z_zone.h"
@@ -718,7 +719,7 @@ void W_NWTDashMerge(char *filename)
             // Replace this entry with an empty string.  This is what
             // nwt -merge does.

-            strcpy(iwad_sprites.lumps[i].name, "");
+            M_StringCopy(iwad_sprites.lumps[i].name, "", 8);
         }
     }