Minor cleanup of PQunescapeBytea(). Avoid unportable assumptions about
authorTom Lane
Fri, 31 Oct 2003 17:43:10 +0000 (17:43 +0000)
committerTom Lane
Fri, 31 Oct 2003 17:43:10 +0000 (17:43 +0000)
behavior of malloc and realloc when request size is 0.  Fix escape
sequence recognizer so that only valid 3-digit octal sequences are
treated as escape sequences ... isdigit() is not a correct test.

src/interfaces/libpq/fe-exec.c

index 1967d7429b44d96ecf2abf05af21ea7c75620f75..d25943ee1114851ae6f49a99ef823d93a0ffae57 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.152 2003/10/19 21:36:41 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.153 2003/10/31 17:43:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2326,21 +2326,21 @@ PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
    return result;
 }
 
-#define VAL(CH) ((CH) - '0')
+#define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
+#define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
+#define OCTVAL(CH) ((CH) - '0')
 
 /*
  *     PQunescapeBytea - converts the null terminated string representation
  *     of a bytea, strtext, into binary, filling a buffer. It returns a
- *     pointer to the buffer which is NULL on error, and the size of the
+ *     pointer to the buffer (or NULL on error), and the size of the
  *     buffer in retbuflen. The pointer may subsequently be used as an
  *     argument to the function free(3). It is the reverse of PQescapeBytea.
  *
  *     The following transformations are made:
- *     \'   == ASCII 39 == '
  *     \\   == ASCII 92 == \
  *     \ooo == a byte whose value = ooo (ooo is an octal number)
  *     \x   == x (x is any character not matched by the above transformations)
- *
  */
 unsigned char *
 PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
@@ -2349,21 +2349,22 @@ PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
                buflen;
    unsigned char *buffer,
               *tmpbuf;
-   int         i,
-               j,
-               byte;
+   size_t      i,
+               j;
 
    if (strtext == NULL)
        return NULL;
 
-   strtextlen = strlen(strtext);       /* will shrink, also we discover
-                                        * if strtext isn't NULL
-                                        * terminated */
-   buffer = (unsigned char *) malloc(strtextlen);
+   strtextlen = strlen(strtext);
+   /*
+    * Length of input is max length of output, but add one to avoid
+    * unportable malloc(0) if input is zero-length.
+    */
+   buffer = (unsigned char *) malloc(strtextlen + 1);
    if (buffer == NULL)
        return NULL;
 
-   for (i = j = buflen = 0; i < (int)strtextlen;)
+   for (i = j = 0; i < strtextlen; )
    {
        switch (strtext[i])
        {
@@ -2373,26 +2374,38 @@ PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
                    buffer[j++] = strtext[i++];
                else
                {
-                   if ((isdigit(strtext[i])) &&
-                       (isdigit(strtext[i + 1])) &&
-                       (isdigit(strtext[i + 2])))
+                   if ((ISFIRSTOCTDIGIT(strtext[i])) &&
+                       (ISOCTDIGIT(strtext[i + 1])) &&
+                       (ISOCTDIGIT(strtext[i + 2])))
                    {
-                       byte = VAL(strtext[i++]);
-                       byte = (byte << 3) + VAL(strtext[i++]);
-                       buffer[j++] = (byte << 3) + VAL(strtext[i++]);
+                       int     byte;
+
+                       byte = OCTVAL(strtext[i++]);
+                       byte = (byte << 3) + OCTVAL(strtext[i++]);
+                       byte = (byte << 3) + OCTVAL(strtext[i++]);
+                       buffer[j++] = byte;
                    }
                }
+               /*
+                * Note: if we see '\' followed by something that isn't
+                * a recognized escape sequence, we loop around having
+                * done nothing except advance i.  Therefore the something
+                * will be emitted as ordinary data on the next cycle.
+                * Corner case: '\' at end of string will just be discarded.
+                */
                break;
 
            default:
                buffer[j++] = strtext[i++];
+               break;
        }
    }
-   buflen = j;                 /* buflen is the length of the unquoted
+   buflen = j;                 /* buflen is the length of the dequoted
                                 * data */
 
    /* Shrink the buffer to be no larger than necessary */
-   tmpbuf = realloc(buffer, buflen);
+   /* +1 avoids unportable behavior when buflen==0 */
+   tmpbuf = realloc(buffer, buflen + 1);
 
    /* It would only be a very brain-dead realloc that could fail, but... */
    if (!tmpbuf)