changeset 27069:b2934c683b5e

(gif_load): Avoid sign extension and thus out of bounds color indices when accessing raster pixels. (gif_image_p, png_image_p, jpeg_image_p, tiff_image_p): Allow only one of :file or :data. (enum pbm_keyword_index): Add PBM_DATA. (pbm_format): Add :data. (pbm_image_p): Allow either :file or :data. (pbm_read_file): New function. (pbm_scan_number): Rewritten to read from string. (pbm_load): Support :data.
author Gerd Moellmann <gerd@gnu.org>
date Sat, 01 Jan 2000 10:08:54 +0000
parents d00d63002726
children c5b14f01cfd0
files src/xfns.c
diffstat 1 files changed, 126 insertions(+), 102 deletions(-) [+]
line wrap: on
line diff
--- a/src/xfns.c	Sat Jan 01 07:14:57 2000 +0000
+++ b/src/xfns.c	Sat Jan 01 10:08:54 2000 +0000
@@ -19,17 +19,6 @@
 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
-/* Ability to read images from memory instead of a file added by
-   William Perry <wmperry@gnu.org> */
-
-/* Image support (XBM, XPM, PBM, JPEG, TIFF, GIF, PNG, GS). tooltips,
-   tool-bars, busy-cursor, file selection dialog added by Gerd
-   Moellmann <gerd@gnu.org>.  */
-
-/* Completely rewritten by Richard Stallman.  */
-
-/* Rewritten for X11 by Joseph Arceneaux */
-
 #include <config.h>
 #include <signal.h>
 #include <stdio.h>
@@ -57,6 +46,8 @@
 #ifdef HAVE_X_WINDOWS
 
 #include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 /* On some systems, the character-composition stuff is broken in X11R5.  */
 
@@ -7599,7 +7590,7 @@
 
 static int pbm_image_p P_ ((Lisp_Object object));
 static int pbm_load P_ ((struct frame *f, struct image *img));
-static int pbm_scan_number P_ ((FILE *fp));
+static int pbm_scan_number P_ ((unsigned char **, unsigned char *));
 
 /* The symbol `pbm' identifying images of this type.  */
 
@@ -7611,6 +7602,7 @@
 {
   PBM_TYPE,
   PBM_FILE,
+  PBM_DATA,
   PBM_ASCENT,
   PBM_MARGIN,
   PBM_RELIEF,
@@ -7625,7 +7617,8 @@
 static struct image_keyword pbm_format[PBM_LAST] =
 {
   {":type",		IMAGE_SYMBOL_VALUE,			1},
-  {":file",		IMAGE_STRING_VALUE,			1},
+  {":file",		IMAGE_STRING_VALUE,			0},
+  {":data",		IMAGE_STRING_VALUE,			0},
   {":ascent",		IMAGE_NON_NEGATIVE_INTEGER_VALUE,	0},
   {":margin",		IMAGE_POSITIVE_INTEGER_VALUE,		0},
   {":relief",		IMAGE_INTEGER_VALUE,			0},
@@ -7659,36 +7652,39 @@
       || (fmt[PBM_ASCENT].count 
 	  && XFASTINT (fmt[PBM_ASCENT].value) > 100))
     return 0;
-  return 1;
-}
-
-
-/* Scan a decimal number from PBM input file FP and return it.  Value
-   is -1 at end of file or if an error occurs.  */
+
+  /* Must specify either :data or :file.  */
+  return fmt[PBM_DATA].count + fmt[PBM_FILE].count == 1;
+}
+
+
+/* Scan a decimal number from *S and return it.  Advance *S while
+   reading the number.  END is the end of the string.  Value is -1 at
+   end of input.  */
 
 static int
-pbm_scan_number (fp)
-     FILE *fp;
+pbm_scan_number (s, end)
+     unsigned char **s, *end;
 {
   int c, val = -1;
 
-  while (!feof (fp))
+  while (*s < end)
     {
       /* Skip white-space.  */
-      while ((c = fgetc (fp)) != EOF && isspace (c))
+      while (*s < end && (c = *(*s)++, isspace (c)))
 	;
 
       if (c == '#')
 	{
 	  /* Skip comment to end of line.  */
-	  while ((c = fgetc (fp)) != EOF && c != '\n')
+	  while (*s < end && (c = *(*s)++, c != '\n'))
 	    ;
 	}
       else if (isdigit (c))
 	{
 	  /* Read decimal number.  */
 	  val = c - '0';
-	  while ((c = fgetc (fp)) != EOF && isdigit (c))
+	  while (*s < end && (c = *(*s)++, isdigit (c)))
 	    val = 10 * val + c - '0';
 	  break;
 	}
@@ -7700,6 +7696,42 @@
 }
 
 
+/* Read FILE into memory.  Value is a pointer to a buffer allocated
+   with xmalloc holding FILE's contents.  Value is null if an error
+   occured.  *SIZE is set to the size of the file.  */
+
+static char *
+pbm_read_file (file, size)
+     Lisp_Object file;
+     int *size;
+{
+  FILE *fp = NULL;
+  char *buf = NULL;
+  struct stat st;
+
+  if (stat (XSTRING (file)->data, &st) == 0
+      && (fp = fopen (XSTRING (file)->data, "r")) != NULL
+      && (buf = (char *) xmalloc (st.st_size),
+	  fread (buf, 1, st.st_size, fp) == st.st_size))
+    {
+      *size = st.st_size;
+      fclose (fp);
+    }
+  else
+    {
+      if (fp)
+	fclose (fp);
+      if (buf)
+	{
+	  xfree (buf);
+	  buf = NULL;
+	}
+    }
+  
+  return buf;
+}
+
+
 /* Load PBM image IMG for use on frame F.  */
 
 static int 
@@ -7707,50 +7739,60 @@
      struct frame *f;
      struct image *img;
 {
-  FILE *fp;
-  char magic[2];
   int raw_p, x, y;
   int width, height, max_color_idx = 0;
   XImage *ximg;
   Lisp_Object file, specified_file;
   enum {PBM_MONO, PBM_GRAY, PBM_COLOR} type;
   struct gcpro gcpro1;
+  unsigned char *contents = NULL;
+  unsigned char *end, *p;
+  int size;
 
   specified_file = image_spec_value (img->spec, QCfile, NULL);
-  file = x_find_image_file (specified_file);
+  file = Qnil;
   GCPRO1 (file);
-  if (!STRINGP (file))
-    {
-      image_error ("Cannot find image file %s", specified_file, Qnil);
-      UNGCPRO;
-      return 0;
-    }
-
-  fp = fopen (XSTRING (file)->data, "r");
-  if (fp == NULL)
-    {
+
+  if (STRINGP (specified_file))
+    {
+      file = x_find_image_file (specified_file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      contents = pbm_read_file (file, &size);
+      if (contents == NULL)
+	{
+	  image_error ("Error reading `%s'", file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      p = contents;
+      end = contents + size;
+    }
+  else
+    {
+      Lisp_Object data;
+      data = image_spec_value (img->spec, QCdata, NULL);
+      p = XSTRING (data)->data;
+      end = p + STRING_BYTES (XSTRING (data));
+    }
+
+  /* Check magic number.  */
+  if (end - p < 2 || *p++ != 'P')
+    {
+      image_error ("Not a PBM image: %s", file, Qnil);
+    error:
+      xfree (contents);
       UNGCPRO;
       return 0;
     }
 
-  /* Read first two characters.  */
-  if (fread (magic, sizeof *magic, 2, fp) != 2)
-    {
-      fclose (fp);
-      image_error ("Not a PBM image file: %s", file, Qnil);
-      UNGCPRO;
-      return 0;
-    }
-
-  if (*magic != 'P')
-    {
-      fclose (fp);
-      image_error ("Not a PBM image file: %s", file, Qnil);
-      UNGCPRO;
-      return 0;
-    }
-
-  switch (magic[1])
+  switch (*p++)
     {
     case '1':
       raw_p = 0, type = PBM_MONO;
@@ -7777,40 +7819,33 @@
       break;
 
     default:
-      fclose (fp);
-      image_error ("Not a PBM image file: %s", file, Qnil);
-      UNGCPRO;
-      return 0;
+      image_error ("Not a PBM image: %s", file, Qnil);
+      goto error;
     }
 
   /* Read width, height, maximum color-component.  Characters
      starting with `#' up to the end of a line are ignored.  */
-  width = pbm_scan_number (fp);
-  height = pbm_scan_number (fp);
+  width = pbm_scan_number (&p, end);
+  height = pbm_scan_number (&p, end);
 
   if (type != PBM_MONO)
     {
-      max_color_idx = pbm_scan_number (fp);
+      max_color_idx = pbm_scan_number (&p, end);
       if (raw_p && max_color_idx > 255)
 	max_color_idx = 255;
     }
   
-  if (width < 0 || height < 0
+  if (width < 0
+      || height < 0
       || (type != PBM_MONO && max_color_idx < 0))
-    {
-      fclose (fp);
-      UNGCPRO;
-      return 0;
-    }
+    goto error;
 
   BLOCK_INPUT;
   if (!x_create_x_image_and_pixmap (f, file, width, height, 0,
 				    &ximg, &img->pixmap))
     {
-      fclose (fp);
       UNBLOCK_INPUT;
-      UNGCPRO;
-      return 0;
+      goto error;
     }
   
   /* Initialize the color hash table.  */
@@ -7826,12 +7861,12 @@
 	    if (raw_p)
 	      {
 		if ((x & 7) == 0)
-		  c = fgetc (fp);
+		  c = *p++;
 		g = c & 0x80;
 		c <<= 1;
 	      }
 	    else
-	      g = pbm_scan_number (fp);
+	      g = pbm_scan_number (&p, end);
 
 	    XPutPixel (ximg, x, y, (g
 				    ? FRAME_FOREGROUND_PIXEL (f)
@@ -7846,31 +7881,29 @@
 	    int r, g, b;
 	    
 	    if (type == PBM_GRAY)
-	      r = g = b = raw_p ? fgetc (fp) : pbm_scan_number (fp);
+	      r = g = b = raw_p ? *p++ : pbm_scan_number (&p, end);
 	    else if (raw_p)
 	      {
-		r = fgetc (fp);
-		g = fgetc (fp);
-		b = fgetc (fp);
+		r = *p++;
+		g = *p++;
+		b = *p++;
 	      }
 	    else
 	      {
-		r = pbm_scan_number (fp);
-		g = pbm_scan_number (fp);
-		b = pbm_scan_number (fp);
+		r = pbm_scan_number (&p, end);
+		g = pbm_scan_number (&p, end);
+		b = pbm_scan_number (&p, end);
 	      }
 	    
 	    if (r < 0 || g < 0 || b < 0)
 	      {
-		fclose (fp);
 		xfree (ximg->data);
 		ximg->data = NULL;
 		XDestroyImage (ximg);
 		UNBLOCK_INPUT;
 		image_error ("Invalid pixel value in file `%s'",
 			     file, Qnil);
-		UNGCPRO;
-		return 0;
+		goto error;
 	      }
 	    
 	    /* RGB values are now in the range 0..max_color_idx.
@@ -7882,8 +7915,6 @@
 	  }
     }
   
-  fclose (fp);
-
   /* Store in IMG->colors the colors allocated for the image, and
      free the color table.  */
   img->colors = colors_in_color_table (&img->ncolors);
@@ -7898,6 +7929,7 @@
   img->height = height;
 
   UNGCPRO;
+  xfree (contents);
   return 1;
 }
 
@@ -7976,10 +8008,8 @@
 	  && XFASTINT (fmt[PNG_ASCENT].value) > 100))
     return 0;
 
-  /* Must specify either the :data or :file keyword.  This should
-     probably be moved up into parse_image_spec, since it seems to be
-     a general requirement. */
-  return fmt[PNG_FILE].count || fmt[PNG_DATA].count;
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[PNG_FILE].count + fmt[PNG_DATA].count == 1;
 }
 
 
@@ -8465,10 +8495,8 @@
 	  && XFASTINT (fmt[JPEG_ASCENT].value) > 100))
     return 0;
 
-  /* Must specify either the :data or :file keyword.  This should
-     probably be moved up into parse_image_spec, since it seems to be
-     a general requirement. */
-  return fmt[JPEG_FILE].count || fmt[JPEG_DATA].count;
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[JPEG_FILE].count + fmt[JPEG_DATA].count == 1;
 }
 
 
@@ -8830,10 +8858,8 @@
 	  && XFASTINT (fmt[TIFF_ASCENT].value) > 100))
     return 0;
   
-  /* Must specify either the :data or :file keyword.  This should
-     probably be moved up into parse_image_spec, since it seems to be
-     a general requirement.  */
-  return fmt[TIFF_FILE].count || fmt[TIFF_DATA].count;
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[TIFF_FILE].count + fmt[TIFF_DATA].count == 1;
 }
 
 
@@ -9154,10 +9180,8 @@
 	  && XFASTINT (fmt[GIF_ASCENT].value) > 100))
     return 0;
   
-  /* Must specify either the :data or :file keyword.  This should
-     probably be moved up into parse_image_spec, since it seems to be
-     a general requirement.  */
-  return fmt[GIF_FILE].count || fmt[GIF_DATA].count;
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[GIF_FILE].count + fmt[GIF_DATA].count == 1;
 }
 
 /* Reading a GIF image from memory