]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/xpmdecod.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / xpmdecod.cpp
index a4b8c82886cc53d0f820c095506a4dfda310a9be..744a5db1e868d39e4fc6d9f2f6d0b40771bbf003 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        xpmdecod.cpp
+// Name:        src/common/xpmdecod.cpp
 // Purpose:     wxXPMDecoder
 // Author:      John Cristy, Vaclav Slavik
 // RCS-ID:      $Id$
@@ -90,57 +90,60 @@ license is as follows:
  * in this Software without prior written authorization from GROUPE BULL.
  */
 
-#ifdef __GNUG__
-#pragma implementation "xpmdecod.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-#  pragma hdrstop
+    #pragma hdrstop
 #endif
 
+#if wxUSE_IMAGE && wxUSE_XPM
+
+#include "wx/xpmdecod.h"
+
 #ifndef WX_PRECOMP
-#  include "wx/defs.h"
+    #include "wx/intl.h"
+    #include "wx/log.h"
+    #include "wx/utils.h"
+    #include "wx/hashmap.h"
+    #include "wx/stream.h"
+    #include "wx/image.h"
+    #include "wx/palette.h"
 #endif
 
-
-#include "wx/stream.h"
-#include "wx/image.h"
-#include "wx/utils.h"
-#include "wx/log.h"
-#include "wx/intl.h"
 #include <string.h>
-
-#include "wx/xpmdecod.h"
+#include <ctype.h>
 
 #if wxUSE_STREAMS
 bool wxXPMDecoder::CanRead(wxInputStream& stream)
 {
     unsigned char buf[9];
 
-    stream.Read(buf, 9);
-    stream.SeekI(-9, wxFromCurrent);
+    if ( !stream.Read(buf, WXSIZEOF(buf)) )
+        return false;
 
-    return (memcmp(buf, "/* XPM */", 9) == 0);
+    return memcmp(buf, "/* XPM */", WXSIZEOF(buf)) == 0;
 }
 
 wxImage wxXPMDecoder::ReadFile(wxInputStream& stream)
 {
     size_t length = stream.GetSize();
-    wxCHECK_MSG(length != 0, wxNullImage, wxT("Cannot read XPM from stream of unknown size"));
+    wxCHECK_MSG( length != 0, wxNullImage,
+                 wxT("Cannot read XPM from stream of unknown size") );
 
-    char *xpm_buffer = new char[length];
-    char *p, *q;
-    size_t i;
+    // use a smart buffer to be sure to free memory even when we return on
+    // error
+    wxCharBuffer buffer(length);
 
-    if ( stream.Read(xpm_buffer, length).LastError() == wxSTREAM_READ_ERROR )
-        return FALSE;
+    char *xpm_buffer = (char *)buffer.data();
+    if ( stream.Read(xpm_buffer, length).GetLastError() == wxSTREAM_READ_ERROR )
+        return wxNullImage;
+    xpm_buffer[length] = '\0';
 
     /*
      *  Remove comments from the file:
      */
+    char *p, *q;
     for (p = xpm_buffer; *p != '\0'; p++)
     {
         if ( (*p == '"') || (*p == '\'') )
@@ -168,13 +171,16 @@ wxImage wxXPMDecoder::ReadFile(wxInputStream& stream)
             if ( (*q == '*') && (*(q + 1) == '/') )
                 break;
         }
-        strcpy(p, q + 2);
+
+        // memmove allows overlaps (unlike strcpy):
+        size_t cpylen = strlen(q + 2) + 1;
+        memmove(p, q + 2, cpylen);
     }
 
     /*
      *  Remove unquoted characters:
      */
-    i = 0;
+    size_t i = 0;
     for (p = xpm_buffer; *p != '\0'; p++)
     {
         if ( *p != '"' )
@@ -202,7 +208,13 @@ wxImage wxXPMDecoder::ReadFile(wxInputStream& stream)
             lines_cnt++;
     }
 
-    xpm_lines = new const char*[lines_cnt];
+    if ( !lines_cnt )
+    {
+        // this doesn't really look an XPM image
+        return wxNullImage;
+    }
+
+    xpm_lines = new const char*[lines_cnt + 1];
     xpm_lines[0] = xpm_buffer;
     line = 1;
     for (p = xpm_buffer; (*p != '\0') && (line < lines_cnt); p++)
@@ -215,17 +227,15 @@ wxImage wxXPMDecoder::ReadFile(wxInputStream& stream)
         }
     }
 
+    xpm_lines[lines_cnt] = NULL;
+
     /*
      *  Read the image:
      */
     wxImage img = ReadData(xpm_lines);
 
-    delete[] xpm_buffer;
-#ifdef __WIN16__
-    delete[] (char**) xpm_lines;
-#else
     delete[] xpm_lines;
-#endif
+
     return img;
 }
 #endif // wxUSE_STREAMS
@@ -248,269 +258,270 @@ wxImage wxXPMDecoder::ReadFile(wxInputStream& stream)
 
 typedef struct
 {
-    char *name;
+    const char *name;
     wxUint32 rgb;
 } rgbRecord;
 
 #define myRGB(r,g,b)   ((wxUint32)r<<16|(wxUint32)g<<8|(wxUint32)b)
 
-static rgbRecord theRGBRecords[] =
+static const rgbRecord theRGBRecords[] =
 {
-    {"AliceBlue", myRGB(240, 248, 255)},
-    {"AntiqueWhite", myRGB(250, 235, 215)},
-    {"Aquamarine", myRGB(50, 191, 193)},
-    {"Azure", myRGB(240, 255, 255)},
-    {"Beige", myRGB(245, 245, 220)},
-    {"Bisque", myRGB(255, 228, 196)},
-    {"Black", myRGB(0, 0, 0)},
-    {"BlanchedAlmond", myRGB(255, 235, 205)},
-    {"Blue", myRGB(0, 0, 255)},
-    {"BlueViolet", myRGB(138, 43, 226)},
-    {"Brown", myRGB(165, 42, 42)},
+    {"aliceblue", myRGB(240, 248, 255)},
+    {"antiquewhite", myRGB(250, 235, 215)},
+    {"aquamarine", myRGB(50, 191, 193)},
+    {"azure", myRGB(240, 255, 255)},
+    {"beige", myRGB(245, 245, 220)},
+    {"bisque", myRGB(255, 228, 196)},
+    {"black", myRGB(0, 0, 0)},
+    {"blanchedalmond", myRGB(255, 235, 205)},
+    {"blue", myRGB(0, 0, 255)},
+    {"blueviolet", myRGB(138, 43, 226)},
+    {"brown", myRGB(165, 42, 42)},
     {"burlywood", myRGB(222, 184, 135)},
-    {"CadetBlue", myRGB(95, 146, 158)},
+    {"cadetblue", myRGB(95, 146, 158)},
     {"chartreuse", myRGB(127, 255, 0)},
     {"chocolate", myRGB(210, 105, 30)},
-    {"Coral", myRGB(255, 114, 86)},
-    {"CornflowerBlue", myRGB(34, 34, 152)},
+    {"coral", myRGB(255, 114, 86)},
+    {"cornflowerblue", myRGB(34, 34, 152)},
     {"cornsilk", myRGB(255, 248, 220)},
-    {"Cyan", myRGB(0, 255, 255)},
-    {"DarkGoldenrod", myRGB(184, 134, 11)},
-    {"DarkGreen", myRGB(0, 86, 45)},
-    {"DarkKhaki", myRGB(189, 183, 107)},
-    {"DarkOliveGreen", myRGB(85, 86, 47)},
-    {"DarkOrange", myRGB(255, 140, 0)},
-    {"DarkOrchid", myRGB(139, 32, 139)},
-    {"DarkSalmon", myRGB(233, 150, 122)},
-    {"DarkSeaGreen", myRGB(143, 188, 143)},
-    {"DarkSlateBlue", myRGB(56, 75, 102)},
-    {"DarkSlateGray", myRGB(47, 79, 79)},
-    {"DarkTurquoise", myRGB(0, 166, 166)},
-    {"DarkViolet", myRGB(148, 0, 211)},
-    {"DeepPink", myRGB(255, 20, 147)},
-    {"DeepSkyBlue", myRGB(0, 191, 255)},
-    {"DimGray", myRGB(84, 84, 84)},
-    {"DodgerBlue", myRGB(30, 144, 255)},
-    {"Firebrick", myRGB(142, 35, 35)},
-    {"FloralWhite", myRGB(255, 250, 240)},
-    {"ForestGreen", myRGB(80, 159, 105)},
+    {"cyan", myRGB(0, 255, 255)},
+    {"darkgoldenrod", myRGB(184, 134, 11)},
+    {"darkgreen", myRGB(0, 86, 45)},
+    {"darkkhaki", myRGB(189, 183, 107)},
+    {"darkolivegreen", myRGB(85, 86, 47)},
+    {"darkorange", myRGB(255, 140, 0)},
+    {"darkorchid", myRGB(139, 32, 139)},
+    {"darksalmon", myRGB(233, 150, 122)},
+    {"darkseagreen", myRGB(143, 188, 143)},
+    {"darkslateblue", myRGB(56, 75, 102)},
+    {"darkslategray", myRGB(47, 79, 79)},
+    {"darkturquoise", myRGB(0, 166, 166)},
+    {"darkviolet", myRGB(148, 0, 211)},
+    {"deeppink", myRGB(255, 20, 147)},
+    {"deepskyblue", myRGB(0, 191, 255)},
+    {"dimgray", myRGB(84, 84, 84)},
+    {"dodgerblue", myRGB(30, 144, 255)},
+    {"firebrick", myRGB(142, 35, 35)},
+    {"floralwhite", myRGB(255, 250, 240)},
+    {"forestgreen", myRGB(80, 159, 105)},
     {"gainsboro", myRGB(220, 220, 220)},
-    {"GhostWhite", myRGB(248, 248, 255)},
-    {"Gold", myRGB(218, 170, 0)},
-    {"Goldenrod", myRGB(239, 223, 132)},
-    {"Gray", myRGB(126, 126, 126)},
-    {"Gray0", myRGB(0, 0, 0)},
-    {"Gray1", myRGB(3, 3, 3)},
-    {"Gray10", myRGB(26, 26, 26)},
-    {"Gray100", myRGB(255, 255, 255)},
-    {"Gray11", myRGB(28, 28, 28)},
-    {"Gray12", myRGB(31, 31, 31)},
-    {"Gray13", myRGB(33, 33, 33)},
-    {"Gray14", myRGB(36, 36, 36)},
-    {"Gray15", myRGB(38, 38, 38)},
-    {"Gray16", myRGB(41, 41, 41)},
-    {"Gray17", myRGB(43, 43, 43)},
-    {"Gray18", myRGB(46, 46, 46)},
-    {"Gray19", myRGB(48, 48, 48)},
-    {"Gray2", myRGB(5, 5, 5)},
-    {"Gray20", myRGB(51, 51, 51)},
-    {"Gray21", myRGB(54, 54, 54)},
-    {"Gray22", myRGB(56, 56, 56)},
-    {"Gray23", myRGB(59, 59, 59)},
-    {"Gray24", myRGB(61, 61, 61)},
-    {"Gray25", myRGB(64, 64, 64)},
-    {"Gray26", myRGB(66, 66, 66)},
-    {"Gray27", myRGB(69, 69, 69)},
-    {"Gray28", myRGB(71, 71, 71)},
-    {"Gray29", myRGB(74, 74, 74)},
-    {"Gray3", myRGB(8, 8, 8)},
-    {"Gray30", myRGB(77, 77, 77)},
-    {"Gray31", myRGB(79, 79, 79)},
-    {"Gray32", myRGB(82, 82, 82)},
-    {"Gray33", myRGB(84, 84, 84)},
-    {"Gray34", myRGB(87, 87, 87)},
-    {"Gray35", myRGB(89, 89, 89)},
-    {"Gray36", myRGB(92, 92, 92)},
-    {"Gray37", myRGB(94, 94, 94)},
-    {"Gray38", myRGB(97, 97, 97)},
-    {"Gray39", myRGB(99, 99, 99)},
-    {"Gray4", myRGB(10, 10, 10)},
-    {"Gray40", myRGB(102, 102, 102)},
-    {"Gray41", myRGB(105, 105, 105)},
-    {"Gray42", myRGB(107, 107, 107)},
-    {"Gray43", myRGB(110, 110, 110)},
-    {"Gray44", myRGB(112, 112, 112)},
-    {"Gray45", myRGB(115, 115, 115)},
-    {"Gray46", myRGB(117, 117, 117)},
-    {"Gray47", myRGB(120, 120, 120)},
-    {"Gray48", myRGB(122, 122, 122)},
-    {"Gray49", myRGB(125, 125, 125)},
-    {"Gray5", myRGB(13, 13, 13)},
-    {"Gray50", myRGB(127, 127, 127)},
-    {"Gray51", myRGB(130, 130, 130)},
-    {"Gray52", myRGB(133, 133, 133)},
-    {"Gray53", myRGB(135, 135, 135)},
-    {"Gray54", myRGB(138, 138, 138)},
-    {"Gray55", myRGB(140, 140, 140)},
-    {"Gray56", myRGB(143, 143, 143)},
-    {"Gray57", myRGB(145, 145, 145)},
-    {"Gray58", myRGB(148, 148, 148)},
-    {"Gray59", myRGB(150, 150, 150)},
-    {"Gray6", myRGB(15, 15, 15)},
-    {"Gray60", myRGB(153, 153, 153)},
-    {"Gray61", myRGB(156, 156, 156)},
-    {"Gray62", myRGB(158, 158, 158)},
-    {"Gray63", myRGB(161, 161, 161)},
-    {"Gray64", myRGB(163, 163, 163)},
-    {"Gray65", myRGB(166, 166, 166)},
-    {"Gray66", myRGB(168, 168, 168)},
-    {"Gray67", myRGB(171, 171, 171)},
-    {"Gray68", myRGB(173, 173, 173)},
-    {"Gray69", myRGB(176, 176, 176)},
-    {"Gray7", myRGB(18, 18, 18)},
-    {"Gray70", myRGB(179, 179, 179)},
-    {"Gray71", myRGB(181, 181, 181)},
-    {"Gray72", myRGB(184, 184, 184)},
-    {"Gray73", myRGB(186, 186, 186)},
-    {"Gray74", myRGB(189, 189, 189)},
-    {"Gray75", myRGB(191, 191, 191)},
-    {"Gray76", myRGB(194, 194, 194)},
-    {"Gray77", myRGB(196, 196, 196)},
-    {"Gray78", myRGB(199, 199, 199)},
-    {"Gray79", myRGB(201, 201, 201)},
-    {"Gray8", myRGB(20, 20, 20)},
-    {"Gray80", myRGB(204, 204, 204)},
-    {"Gray81", myRGB(207, 207, 207)},
-    {"Gray82", myRGB(209, 209, 209)},
-    {"Gray83", myRGB(212, 212, 212)},
-    {"Gray84", myRGB(214, 214, 214)},
-    {"Gray85", myRGB(217, 217, 217)},
-    {"Gray86", myRGB(219, 219, 219)},
-    {"Gray87", myRGB(222, 222, 222)},
-    {"Gray88", myRGB(224, 224, 224)},
-    {"Gray89", myRGB(227, 227, 227)},
-    {"Gray9", myRGB(23, 23, 23)},
-    {"Gray90", myRGB(229, 229, 229)},
-    {"Gray91", myRGB(232, 232, 232)},
-    {"Gray92", myRGB(235, 235, 235)},
-    {"Gray93", myRGB(237, 237, 237)},
-    {"Gray94", myRGB(240, 240, 240)},
-    {"Gray95", myRGB(242, 242, 242)},
-    {"Gray96", myRGB(245, 245, 245)},
-    {"Gray97", myRGB(247, 247, 247)},
-    {"Gray98", myRGB(250, 250, 250)},
-    {"Gray99", myRGB(252, 252, 252)},
-    {"Green", myRGB(0, 255, 0)},
-    {"GreenYellow", myRGB(173, 255, 47)},
+    {"ghostwhite", myRGB(248, 248, 255)},
+    {"gold", myRGB(218, 170, 0)},
+    {"goldenrod", myRGB(239, 223, 132)},
+    {"gray", myRGB(126, 126, 126)},
+    {"gray0", myRGB(0, 0, 0)},
+    {"gray1", myRGB(3, 3, 3)},
+    {"gray10", myRGB(26, 26, 26)},
+    {"gray100", myRGB(255, 255, 255)},
+    {"gray11", myRGB(28, 28, 28)},
+    {"gray12", myRGB(31, 31, 31)},
+    {"gray13", myRGB(33, 33, 33)},
+    {"gray14", myRGB(36, 36, 36)},
+    {"gray15", myRGB(38, 38, 38)},
+    {"gray16", myRGB(41, 41, 41)},
+    {"gray17", myRGB(43, 43, 43)},
+    {"gray18", myRGB(46, 46, 46)},
+    {"gray19", myRGB(48, 48, 48)},
+    {"gray2", myRGB(5, 5, 5)},
+    {"gray20", myRGB(51, 51, 51)},
+    {"gray21", myRGB(54, 54, 54)},
+    {"gray22", myRGB(56, 56, 56)},
+    {"gray23", myRGB(59, 59, 59)},
+    {"gray24", myRGB(61, 61, 61)},
+    {"gray25", myRGB(64, 64, 64)},
+    {"gray26", myRGB(66, 66, 66)},
+    {"gray27", myRGB(69, 69, 69)},
+    {"gray28", myRGB(71, 71, 71)},
+    {"gray29", myRGB(74, 74, 74)},
+    {"gray3", myRGB(8, 8, 8)},
+    {"gray30", myRGB(77, 77, 77)},
+    {"gray31", myRGB(79, 79, 79)},
+    {"gray32", myRGB(82, 82, 82)},
+    {"gray33", myRGB(84, 84, 84)},
+    {"gray34", myRGB(87, 87, 87)},
+    {"gray35", myRGB(89, 89, 89)},
+    {"gray36", myRGB(92, 92, 92)},
+    {"gray37", myRGB(94, 94, 94)},
+    {"gray38", myRGB(97, 97, 97)},
+    {"gray39", myRGB(99, 99, 99)},
+    {"gray4", myRGB(10, 10, 10)},
+    {"gray40", myRGB(102, 102, 102)},
+    {"gray41", myRGB(105, 105, 105)},
+    {"gray42", myRGB(107, 107, 107)},
+    {"gray43", myRGB(110, 110, 110)},
+    {"gray44", myRGB(112, 112, 112)},
+    {"gray45", myRGB(115, 115, 115)},
+    {"gray46", myRGB(117, 117, 117)},
+    {"gray47", myRGB(120, 120, 120)},
+    {"gray48", myRGB(122, 122, 122)},
+    {"gray49", myRGB(125, 125, 125)},
+    {"gray5", myRGB(13, 13, 13)},
+    {"gray50", myRGB(127, 127, 127)},
+    {"gray51", myRGB(130, 130, 130)},
+    {"gray52", myRGB(133, 133, 133)},
+    {"gray53", myRGB(135, 135, 135)},
+    {"gray54", myRGB(138, 138, 138)},
+    {"gray55", myRGB(140, 140, 140)},
+    {"gray56", myRGB(143, 143, 143)},
+    {"gray57", myRGB(145, 145, 145)},
+    {"gray58", myRGB(148, 148, 148)},
+    {"gray59", myRGB(150, 150, 150)},
+    {"gray6", myRGB(15, 15, 15)},
+    {"gray60", myRGB(153, 153, 153)},
+    {"gray61", myRGB(156, 156, 156)},
+    {"gray62", myRGB(158, 158, 158)},
+    {"gray63", myRGB(161, 161, 161)},
+    {"gray64", myRGB(163, 163, 163)},
+    {"gray65", myRGB(166, 166, 166)},
+    {"gray66", myRGB(168, 168, 168)},
+    {"gray67", myRGB(171, 171, 171)},
+    {"gray68", myRGB(173, 173, 173)},
+    {"gray69", myRGB(176, 176, 176)},
+    {"gray7", myRGB(18, 18, 18)},
+    {"gray70", myRGB(179, 179, 179)},
+    {"gray71", myRGB(181, 181, 181)},
+    {"gray72", myRGB(184, 184, 184)},
+    {"gray73", myRGB(186, 186, 186)},
+    {"gray74", myRGB(189, 189, 189)},
+    {"gray75", myRGB(191, 191, 191)},
+    {"gray76", myRGB(194, 194, 194)},
+    {"gray77", myRGB(196, 196, 196)},
+    {"gray78", myRGB(199, 199, 199)},
+    {"gray79", myRGB(201, 201, 201)},
+    {"gray8", myRGB(20, 20, 20)},
+    {"gray80", myRGB(204, 204, 204)},
+    {"gray81", myRGB(207, 207, 207)},
+    {"gray82", myRGB(209, 209, 209)},
+    {"gray83", myRGB(212, 212, 212)},
+    {"gray84", myRGB(214, 214, 214)},
+    {"gray85", myRGB(217, 217, 217)},
+    {"gray86", myRGB(219, 219, 219)},
+    {"gray87", myRGB(222, 222, 222)},
+    {"gray88", myRGB(224, 224, 224)},
+    {"gray89", myRGB(227, 227, 227)},
+    {"gray9", myRGB(23, 23, 23)},
+    {"gray90", myRGB(229, 229, 229)},
+    {"gray91", myRGB(232, 232, 232)},
+    {"gray92", myRGB(235, 235, 235)},
+    {"gray93", myRGB(237, 237, 237)},
+    {"gray94", myRGB(240, 240, 240)},
+    {"gray95", myRGB(242, 242, 242)},
+    {"gray96", myRGB(245, 245, 245)},
+    {"gray97", myRGB(247, 247, 247)},
+    {"gray98", myRGB(250, 250, 250)},
+    {"gray99", myRGB(252, 252, 252)},
+    {"green", myRGB(0, 255, 0)},
+    {"greenyellow", myRGB(173, 255, 47)},
     {"honeydew", myRGB(240, 255, 240)},
-    {"HotPink", myRGB(255, 105, 180)},
-    {"IndianRed", myRGB(107, 57, 57)},
+    {"hotpink", myRGB(255, 105, 180)},
+    {"indianred", myRGB(107, 57, 57)},
     {"ivory", myRGB(255, 255, 240)},
-    {"Khaki", myRGB(179, 179, 126)},
+    {"khaki", myRGB(179, 179, 126)},
     {"lavender", myRGB(230, 230, 250)},
-    {"LavenderBlush", myRGB(255, 240, 245)},
-    {"LawnGreen", myRGB(124, 252, 0)},
-    {"LemonChiffon", myRGB(255, 250, 205)},
-    {"LightBlue", myRGB(176, 226, 255)},
-    {"LightCoral", myRGB(240, 128, 128)},
-    {"LightCyan", myRGB(224, 255, 255)},
-    {"LightGoldenrod", myRGB(238, 221, 130)},
-    {"LightGoldenrodYellow", myRGB(250, 250, 210)},
-    {"LightGray", myRGB(168, 168, 168)},
-    {"LightPink", myRGB(255, 182, 193)},
-    {"LightSalmon", myRGB(255, 160, 122)},
-    {"LightSeaGreen", myRGB(32, 178, 170)},
-    {"LightSkyBlue", myRGB(135, 206, 250)},
-    {"LightSlateBlue", myRGB(132, 112, 255)},
-    {"LightSlateGray", myRGB(119, 136, 153)},
-    {"LightSteelBlue", myRGB(124, 152, 211)},
-    {"LightYellow", myRGB(255, 255, 224)},
-    {"LimeGreen", myRGB(0, 175, 20)},
+    {"lavenderblush", myRGB(255, 240, 245)},
+    {"lawngreen", myRGB(124, 252, 0)},
+    {"lemonchiffon", myRGB(255, 250, 205)},
+    {"lightblue", myRGB(176, 226, 255)},
+    {"lightcoral", myRGB(240, 128, 128)},
+    {"lightcyan", myRGB(224, 255, 255)},
+    {"lightgoldenrod", myRGB(238, 221, 130)},
+    {"lightgoldenrodyellow", myRGB(250, 250, 210)},
+    {"lightgray", myRGB(168, 168, 168)},
+    {"lightpink", myRGB(255, 182, 193)},
+    {"lightsalmon", myRGB(255, 160, 122)},
+    {"lightseagreen", myRGB(32, 178, 170)},
+    {"lightskyblue", myRGB(135, 206, 250)},
+    {"lightslateblue", myRGB(132, 112, 255)},
+    {"lightslategray", myRGB(119, 136, 153)},
+    {"lightsteelblue", myRGB(124, 152, 211)},
+    {"lightyellow", myRGB(255, 255, 224)},
+    {"limegreen", myRGB(0, 175, 20)},
     {"linen", myRGB(250, 240, 230)},
-    {"Magenta", myRGB(255, 0, 255)},
-    {"Maroon", myRGB(143, 0, 82)},
-    {"MediumAquamarine", myRGB(0, 147, 143)},
-    {"MediumBlue", myRGB(50, 50, 204)},
-    {"MediumForestGreen", myRGB(50, 129, 75)},
-    {"MediumGoldenrod", myRGB(209, 193, 102)},
-    {"MediumOrchid", myRGB(189, 82, 189)},
-    {"MediumPurple", myRGB(147, 112, 219)},
-    {"MediumSeaGreen", myRGB(52, 119, 102)},
-    {"MediumSlateBlue", myRGB(106, 106, 141)},
-    {"MediumSpringGreen", myRGB(35, 142, 35)},
-    {"MediumTurquoise", myRGB(0, 210, 210)},
-    {"MediumVioletRed", myRGB(213, 32, 121)},
-    {"MidnightBlue", myRGB(47, 47, 100)},
-    {"MintCream", myRGB(245, 255, 250)},
-    {"MistyRose", myRGB(255, 228, 225)},
+    {"magenta", myRGB(255, 0, 255)},
+    {"maroon", myRGB(143, 0, 82)},
+    {"mediumaquamarine", myRGB(0, 147, 143)},
+    {"mediumblue", myRGB(50, 50, 204)},
+    {"mediumforestgreen", myRGB(50, 129, 75)},
+    {"mediumgoldenrod", myRGB(209, 193, 102)},
+    {"mediumorchid", myRGB(189, 82, 189)},
+    {"mediumpurple", myRGB(147, 112, 219)},
+    {"mediumseagreen", myRGB(52, 119, 102)},
+    {"mediumslateblue", myRGB(106, 106, 141)},
+    {"mediumspringgreen", myRGB(35, 142, 35)},
+    {"mediumturquoise", myRGB(0, 210, 210)},
+    {"mediumvioletred", myRGB(213, 32, 121)},
+    {"midnightblue", myRGB(47, 47, 100)},
+    {"mintcream", myRGB(245, 255, 250)},
+    {"mistyrose", myRGB(255, 228, 225)},
     {"moccasin", myRGB(255, 228, 181)},
-    {"NavajoWhite", myRGB(255, 222, 173)},
-    {"Navy", myRGB(35, 35, 117)},
-    {"NavyBlue", myRGB(35, 35, 117)},
-    {"OldLace", myRGB(253, 245, 230)},
-    {"OliveDrab", myRGB(107, 142, 35)},
-    {"Orange", myRGB(255, 135, 0)},
-    {"OrangeRed", myRGB(255, 69, 0)},
-    {"Orchid", myRGB(239, 132, 239)},
-    {"PaleGoldenrod", myRGB(238, 232, 170)},
-    {"PaleGreen", myRGB(115, 222, 120)},
-    {"PaleTurquoise", myRGB(175, 238, 238)},
-    {"PaleVioletRed", myRGB(219, 112, 147)},
-    {"PapayaWhip", myRGB(255, 239, 213)},
-    {"PeachPuff", myRGB(255, 218, 185)},
+    {"navajowhite", myRGB(255, 222, 173)},
+    {"navy", myRGB(35, 35, 117)},
+    {"navyblue", myRGB(35, 35, 117)},
+    {"oldlace", myRGB(253, 245, 230)},
+    {"olivedrab", myRGB(107, 142, 35)},
+    {"orange", myRGB(255, 135, 0)},
+    {"orangered", myRGB(255, 69, 0)},
+    {"orchid", myRGB(239, 132, 239)},
+    {"palegoldenrod", myRGB(238, 232, 170)},
+    {"palegreen", myRGB(115, 222, 120)},
+    {"paleturquoise", myRGB(175, 238, 238)},
+    {"palevioletred", myRGB(219, 112, 147)},
+    {"papayawhip", myRGB(255, 239, 213)},
+    {"peachpuff", myRGB(255, 218, 185)},
     {"peru", myRGB(205, 133, 63)},
-    {"Pink", myRGB(255, 181, 197)},
-    {"Plum", myRGB(197, 72, 155)},
-    {"PowderBlue", myRGB(176, 224, 230)},
+    {"pink", myRGB(255, 181, 197)},
+    {"plum", myRGB(197, 72, 155)},
+    {"powderblue", myRGB(176, 224, 230)},
     {"purple", myRGB(160, 32, 240)},
-    {"Red", myRGB(255, 0, 0)},
-    {"RosyBrown", myRGB(188, 143, 143)},
-    {"RoyalBlue", myRGB(65, 105, 225)},
-    {"SaddleBrown", myRGB(139, 69, 19)},
-    {"Salmon", myRGB(233, 150, 122)},
-    {"SandyBrown", myRGB(244, 164, 96)},
-    {"SeaGreen", myRGB(82, 149, 132)},
+    {"red", myRGB(255, 0, 0)},
+    {"rosybrown", myRGB(188, 143, 143)},
+    {"royalblue", myRGB(65, 105, 225)},
+    {"saddlebrown", myRGB(139, 69, 19)},
+    {"salmon", myRGB(233, 150, 122)},
+    {"sandybrown", myRGB(244, 164, 96)},
+    {"seagreen", myRGB(82, 149, 132)},
     {"seashell", myRGB(255, 245, 238)},
-    {"Sienna", myRGB(150, 82, 45)},
-    {"SkyBlue", myRGB(114, 159, 255)},
-    {"SlateBlue", myRGB(126, 136, 171)},
-    {"SlateGray", myRGB(112, 128, 144)},
+    {"sienna", myRGB(150, 82, 45)},
+    {"silver", myRGB(192, 192, 192)},
+    {"skyblue", myRGB(114, 159, 255)},
+    {"slateblue", myRGB(126, 136, 171)},
+    {"slategray", myRGB(112, 128, 144)},
     {"snow", myRGB(255, 250, 250)},
-    {"SpringGreen", myRGB(65, 172, 65)},
-    {"SteelBlue", myRGB(84, 112, 170)},
-    {"Tan", myRGB(222, 184, 135)},
-    {"Thistle", myRGB(216, 191, 216)},
+    {"springgreen", myRGB(65, 172, 65)},
+    {"steelblue", myRGB(84, 112, 170)},
+    {"tan", myRGB(222, 184, 135)},
+    {"thistle", myRGB(216, 191, 216)},
     {"tomato", myRGB(255, 99, 71)},
-    {"Transparent", myRGB(0, 0, 1)},
-    {"Turquoise", myRGB(25, 204, 223)},
-    {"Violet", myRGB(156, 62, 206)},
-    {"VioletRed", myRGB(243, 62, 150)},
-    {"Wheat", myRGB(245, 222, 179)},
-    {"White", myRGB(255, 255, 255)},
-    {"WhiteSmoke", myRGB(245, 245, 245)},
-    {"Yellow", myRGB(255, 255, 0)},
-    {"YellowGreen", myRGB(50, 216, 56)},
+    {"transparent", myRGB(0, 0, 1)},
+    {"turquoise", myRGB(25, 204, 223)},
+    {"violet", myRGB(156, 62, 206)},
+    {"violetred", myRGB(243, 62, 150)},
+    {"wheat", myRGB(245, 222, 179)},
+    {"white", myRGB(255, 255, 255)},
+    {"whitesmoke", myRGB(245, 245, 245)},
+    {"yellow", myRGB(255, 255, 0)},
+    {"yellowgreen", myRGB(50, 216, 56)},
     {NULL, myRGB(0, 0, 0)}
 };
-static int numTheRGBRecords = 234;
+static const int numTheRGBRecords = 235;
 
 static unsigned char ParseHexadecimal(char digit1, char digit2)
 {
     unsigned char i1, i2;
 
     if (digit1 >= 'a')
-        i1 = digit1 - 'a' + 0x0A;
+        i1 = (unsigned char)(digit1 - 'a' + 0x0A);
     else if (digit1 >= 'A')
-        i1 = digit1 - 'A' + 0x0A;
+        i1 = (unsigned char)(digit1 - 'A' + 0x0A);
     else
-        i1 = digit1 - '0';
+        i1 = (unsigned char)(digit1 - '0');
     if (digit2 >= 'a')
-        i2 = digit2 - 'a' + 0x0A;
+        i2 = (unsigned char)(digit2 - 'a' + 0x0A);
     else if (digit2 >= 'A')
-        i2 = digit2 - 'A' + 0x0A;
+        i2 = (unsigned char)(digit2 - 'A' + 0x0A);
     else
-        i2 = digit2 - '0';
-    return (0x10 * i1 + i2);
+        i2 = (unsigned char)(digit2 - '0');
+    return (unsigned char)(0x10 * i1 + i2);
 }
 
 static bool GetRGBFromName(const char *inname, bool *isNone,
@@ -522,17 +533,19 @@ static bool GetRGBFromName(const char *inname, bool *isNone,
     char *name;
     char *grey, *p;
 
-    // #rrggbb are not in database, we parse them directly
-    if ( *inname == '#' && strlen(inname) == 7 )
+    // Neither #rrggbb nor #rrrrggggbbbb are in database, we parse them directly
+    size_t inname_len = strlen(inname);
+    if ( *inname == '#' && (inname_len == 7 || inname_len == 13))
     {
+        size_t ofs = (inname_len == 7) ? 2 : 4;
         *r = ParseHexadecimal(inname[1], inname[2]);
-        *g = ParseHexadecimal(inname[3], inname[4]);
-        *b = ParseHexadecimal(inname[5], inname[6]);
-        *isNone = FALSE;
-        return TRUE;
+        *g = ParseHexadecimal(inname[1*ofs+1], inname[1*ofs+2]);
+        *b = ParseHexadecimal(inname[2*ofs+1], inname[2*ofs+2]);
+        *isNone = false;
+        return true;
     }
 
-    name = strdup(inname);
+    name = wxStrdupA(inname);
 
     // theRGBRecords[] has no names with spaces, and no grey, but a
     // lot of gray...
@@ -550,7 +563,7 @@ static bool GetRGBFromName(const char *inname, bool *isNone,
     p = name;
     while (*p)
     {
-        *p = tolower(*p);
+        *p = (char)tolower(*p);
         p++;
     }
 
@@ -563,12 +576,12 @@ static bool GetRGBFromName(const char *inname, bool *isNone,
     bool found;
     if ( strcmp(name, "none") == 0 )
     {
-        *isNone = TRUE;
-        found = TRUE;
+        *isNone = true;
+        found = true;
     }
     else // not "None"
     {
-        found = FALSE;
+        found = false;
 
         // binary search:
         left = 0;
@@ -583,8 +596,8 @@ static bool GetRGBFromName(const char *inname, bool *isNone,
                 *r = (unsigned char)((rgbVal >> 16) & 0xFF);
                 *g = (unsigned char)((rgbVal >> 8) & 0xFF);
                 *b = (unsigned char)((rgbVal) & 0xFF);
-                *isNone = FALSE;
-                found = TRUE;
+                *isNone = false;
+                found = true;
                 break;
             }
             else if ( cmp < 0 )
@@ -605,7 +618,7 @@ static bool GetRGBFromName(const char *inname, bool *isNone,
 
 static const char *ParseColor(const char *data)
 {
-    static const char *targets[] =
+    static const char *const targets[] =
                         {"c ", "g ", "g4 ", "m ", "b ", "s ", NULL};
 
     const char *p, *r;
@@ -635,33 +648,37 @@ static const char *ParseColor(const char *data)
     return NULL;
 }
 
-class wxXPMColourMapData : public wxObject
+struct wxXPMColourMapData
 {
-    public:
-        unsigned char R,G,B;
+    wxXPMColourMapData() { R = G = B = 0; }
+    unsigned char R,G,B;
 };
+WX_DECLARE_STRING_HASH_MAP(wxXPMColourMapData, wxXPMColourMap);
 
-wxImage wxXPMDecoder::ReadData(const char **xpm_data)
+wxImage wxXPMDecoder::ReadData(const char* const* xpm_data)
 {
+    wxCHECK_MSG(xpm_data, wxNullImage, wxT("NULL XPM data") );
+
     wxImage img;
     int count;
     unsigned width, height, colors_cnt, chars_per_pixel;
     size_t i, j, i_key;
-    wxChar key[64];
+    char key[64];
     const char *clr_def;
-    bool hasMask;
-    wxXPMColourMapData *clr_data;
-    wxHashTable clr_tbl(wxKEY_STRING);
+    wxXPMColourMap clr_tbl;
+    wxXPMColourMap::iterator it;
+    wxString maskKey;
+    wxString keyString;
 
     /*
      *  Read hints and initialize structures:
      */
-     
+
     count = sscanf(xpm_data[0], "%u %u %u %u",
                    &width, &height, &colors_cnt, &chars_per_pixel);
     if ( count != 4 || width * height * colors_cnt == 0 )
     {
-        wxLogError(_T("XPM: Not XPM data!"));
+        wxLogError(_("XPM: incorrect header format!"));
         return wxNullImage;
     }
 
@@ -670,56 +687,82 @@ wxImage wxXPMDecoder::ReadData(const char **xpm_data)
     //     8bit RGB...
     wxCHECK_MSG(chars_per_pixel < 64, wxNullImage, wxT("XPM colormaps this large not supported."));
 
-    img.Create(width, height);
-    if ( !img.Ok() ) return img;
+    if (!img.Create(width, height, false))
+        return wxNullImage;
 
-    img.SetMask(FALSE);
-    key[chars_per_pixel] = wxT('\0');
-    hasMask = FALSE;
-    clr_tbl.DeleteContents(TRUE);
+    key[chars_per_pixel] = '\0';
 
     /*
      *  Create colour map:
      */
+    wxXPMColourMapData clr_data;
     for (i = 0; i < colors_cnt; i++)
     {
+        const char *xmpColLine = xpm_data[1 + i];
+
+        // we must have at least " x y" after the colour index, hence +5
+        if ( !xmpColLine || strlen(xmpColLine) < chars_per_pixel + 5 )
+        {
+            wxLogError(_("XPM: incorrect colour description in line %d"),
+                       (int)(1 + i));
+            return wxNullImage;
+        }
+
         for (i_key = 0; i_key < chars_per_pixel; i_key++)
-            key[i_key] = (wxChar)xpm_data[1 + i][i_key];
-        clr_def = ParseColor(xpm_data[1 + i]);
-        clr_data = new wxXPMColourMapData;
+            key[i_key] = xmpColLine[i_key];
+        clr_def = ParseColor(xmpColLine + chars_per_pixel);
 
         if ( clr_def == NULL )
         {
-            wxLogError(_("XPM: malformed colour definition '%s'!"), xpm_data[1+i]);
-            clr_data->R = 255, clr_data->G = 0, clr_data->B = 255;
+            wxLogError(_("XPM: malformed colour definition '%s' at line %d!"),
+                       xmpColLine, (int)(1 + i));
+            return wxNullImage;
         }
-        else
+
+        bool isNone = false;
+        if ( !GetRGBFromName(clr_def, &isNone,
+                             &clr_data.R, &clr_data.G, &clr_data.B) )
         {
-            bool isNone;
-            if ( !GetRGBFromName(clr_def, &isNone,
-                                 &clr_data->R, &clr_data->G, &clr_data->B) )
-            {
-                wxLogError(_("XPM: malformed colour definition '%s'!"), xpm_data[1+i]);
-                clr_data->R = 255, clr_data->G = 0, clr_data->B = 255;
-            }
-            else
-            {
-                if ( isNone )
-                {
-                    img.SetMask(TRUE);
-                    img.SetMaskColour(255, 0, 255);
-                    hasMask = TRUE;
-                    clr_data->R = 255, clr_data->G = 0, clr_data->B = 255;
-                }
-                else
-                {
-                    if ( hasMask && clr_data->R == 255 &&
-                                    clr_data->G == 0 && clr_data->B == 255 )
-                        clr_data->B = 254;
-                }
-            }
+            wxLogError(_("XPM: malformed colour definition '%s' at line %d!"),
+                       xmpColLine, (int)(1 + i));
+            return wxNullImage;
         }
-        clr_tbl.Put(key, clr_data);
+
+        keyString = key;
+        if ( isNone )
+            maskKey = keyString;
+
+        clr_tbl[keyString] = clr_data;
+    }
+
+    // deal with the mask: we must replace pseudo-colour "None" with the mask
+    // colour (which can be any colour not otherwise used in the image)
+    if (!maskKey.empty())
+    {
+        wxLongToLongHashMap rgb_table;
+        long rgb;
+        const size_t n = clr_tbl.size();
+        wxXPMColourMap::const_iterator iter = clr_tbl.begin();
+        for (i = 0; i < n; ++i, ++iter)
+        {
+            const wxXPMColourMapData& data = iter->second;
+            rgb = (data.R << 16) + (data.G << 8) + data.B;
+            rgb_table[rgb];
+        }
+        for (rgb = 0; rgb <= 0xffffff && rgb_table.count(rgb); ++rgb)
+            ;
+        if (rgb > 0xffffff)
+        {
+            wxLogError(_("XPM: no colors left to use for mask!"));
+            return wxNullImage;
+        }
+
+        wxXPMColourMapData& maskData = clr_tbl[maskKey];
+        maskData.R = wxByte(rgb >> 16);
+        maskData.G = wxByte(rgb >> 8);
+        maskData.B = wxByte(rgb);
+
+        img.SetMaskColour(maskData.R, maskData.G, maskData.B);
     }
 
     /*
@@ -727,28 +770,62 @@ wxImage wxXPMDecoder::ReadData(const char **xpm_data)
      */
 
     unsigned char *img_data = img.GetData();
+    wxXPMColourMap::iterator entry;
+    wxXPMColourMap::iterator end = clr_tbl.end();
+
     for (j = 0; j < height; j++)
     {
         for (i = 0; i < width; i++, img_data += 3)
         {
+            const char *xpmImgLine = xpm_data[1 + colors_cnt + j];
+            if ( !xpmImgLine || strlen(xpmImgLine) < width*chars_per_pixel )
+            {
+                wxLogError(_("XPM: truncated image data at line %d!"),
+                           (int)(1 + colors_cnt + j));
+                return wxNullImage;
+            }
+
             for (i_key = 0; i_key < chars_per_pixel; i_key++)
-                key[i_key] = (wxChar)xpm_data[1 + colors_cnt + j]
-                                             [chars_per_pixel * i + i_key];
-            clr_data = (wxXPMColourMapData*) clr_tbl.Get(key);
-            if ( clr_data == NULL )
             {
-                wxLogError(_("XPM: Malformed pixel data!"));
+                key[i_key] = xpmImgLine[chars_per_pixel * i + i_key];
             }
-            else
+
+            keyString = key;
+            entry = clr_tbl.find(keyString);
+            if ( entry == end )
             {
-                img_data[0] = clr_data->R;
-                img_data[1] = clr_data->G;
-                img_data[2] = clr_data->B;
+                wxLogError(_("XPM: Malformed pixel data!"));
+
+                // better return right now as otherwise we risk to flood the
+                // user with error messages as something seems to be seriously
+                // wrong with the file and so we could give this message for
+                // each remaining pixel if we don't bail out
+                return wxNullImage;
             }
+
+            img_data[0] = entry->second.R;
+            img_data[1] = entry->second.G;
+            img_data[2] = entry->second.B;
         }
     }
+#if wxUSE_PALETTE
+    unsigned char* r = new unsigned char[colors_cnt];
+    unsigned char* g = new unsigned char[colors_cnt];
+    unsigned char* b = new unsigned char[colors_cnt];
 
+    for (it = clr_tbl.begin(), i = 0; it != clr_tbl.end(); it++, i++)
+    {
+        r[i] = it->second.R;
+        g[i] = it->second.G;
+        b[i] = it->second.B;
+    }
+    wxASSERT(i == colors_cnt);
+    img.SetPalette(wxPalette(colors_cnt, r, g, b));
+    delete[] r;
+    delete[] g;
+    delete[] b;
+#endif // wxUSE_PALETTE
     return img;
 }
 
-
+#endif // wxUSE_IMAGE && wxUSE_XPM