As we're not passing over (or copying in) a NUL, don't need that extra
[p5sagit/p5-mst-13.2.git] / ext / MIME / Base64 / Base64.xs
index d540f11..afbad93 100644 (file)
@@ -1,6 +1,6 @@
-/* $Id: Base64.xs,v 1.36 2003/05/13 16:21:25 gisle Exp $
+/* $Id: Base64.xs,v 3.5 2005/11/26 10:44:14 gisle Exp $
 
-Copyright 1997-2003 Gisle Aas
+Copyright 1997-2004 Gisle Aas
 
 This library is free software; you can redistribute it and/or
 modify it under the same terms as Perl itself.
@@ -28,6 +28,7 @@ metamail, which comes with this message:
 #ifdef __cplusplus
 extern "C" {
 #endif
+#define PERL_NO_GET_CONTEXT     /* we want efficiency */
 #include "EXTERN.h"
 #include "perl.h"
 #include "XSUB.h"
@@ -55,14 +56,14 @@ extern "C" {
 
 #define MAX_LINE  76 /* size of encoded lines */
 
-static char basis_64[] =
+static const char basis_64[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 #define XX      255    /* illegal base64 char */
 #define EQ      254    /* padding */
 #define INVALID XX
 
-static unsigned char index_64[256] = {
+static const unsigned char index_64[256] = {
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
@@ -100,6 +101,10 @@ static unsigned char index_64[256] = {
 #   define SvPVbyte SvPV
 #endif
 
+#ifndef isXDIGIT
+#   define isXDIGIT isxdigit
+#endif
+
 #ifndef NATIVE_TO_ASCII
 #   define NATIVE_TO_ASCII(ch) (ch)
 #endif
@@ -159,7 +164,7 @@ encode_base64(sv,...)
                chunk = 0;
            }
            c1 = *str++;
-           c2 = *str++;
+           c2 = len > 1 ? *str++ : '\0';
            *r++ = basis_64[c1>>2];
            *r++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
            if (len > 2) {
@@ -253,16 +258,21 @@ decode_base64(sv)
 
 MODULE = MIME::Base64          PACKAGE = MIME::QuotedPrint
 
-#define qp_isplain(c) ((c) == '\t' || ((c) >= ' ' && (c) <= '~') && (c) != '=')
+#ifdef EBCDIC
+#define qp_isplain(c) ((c) == '\t' || ((!isprint(c) && (c) != '=')))
+#else
+#define qp_isplain(c) ((c) == '\t' || (((c) >= ' ' && (c) <= '~') && (c) != '='))
+#endif
 
 SV*
 encode_qp(sv,...)
        SV* sv
-       PROTOTYPE: $;$
+       PROTOTYPE: $;$$
 
        PREINIT:
        char *eol;
        STRLEN eol_len;
+       int binary;
        STRLEN sv_len;
        STRLEN linelen;
        char *beg;
@@ -283,6 +293,8 @@ encode_qp(sv,...)
            eol_len = 1;
        }
 
+       binary = (items > 2 && SvTRUE(ST(2)));
+
        beg = SvPV(sv, sv_len);
        end = beg + sv_len;
 
@@ -298,7 +310,7 @@ encode_qp(sv,...)
            while (p < end && qp_isplain(*p)) {
                p++;
            }
-           if (*p == '\n' || p == end) {
+           if (p == end || *p == '\n') {
                /* whitespace at end of line must be encoded */
                while (p > p_beg && (*(p - 1) == '\t' || *(p - 1) == ' '))
                    p--;
@@ -308,9 +320,9 @@ encode_qp(sv,...)
            if (p_len) {
                /* output plain text (with line breaks) */
                if (eol_len) {
-                   STRLEN max_last_line = (*p == '\n' || p == end)
+                   STRLEN max_last_line = (p == end || *p == '\n')
                                              ? MAX_LINE         /* .......\n */
-                                             : (*(p + 1) == '\n' || (p + 1) == end)
+                                             : ((p + 1) == end || *(p + 1) == '\n')
                                                ? MAX_LINE - 3   /* ....=XX\n */
                                                : MAX_LINE - 4;  /* ...=XX=\n */
                    while (p_len + linelen > max_last_line) {
@@ -331,13 +343,17 @@ encode_qp(sv,...)
                }
            }
 
-           if (*p == '\n' && eol_len) {
+           if (p == end) {
+               break;
+            }
+           else if (*p == '\n' && eol_len && !binary) {
                sv_catpvn(RETVAL, eol, eol_len);
                p++;
                linelen = 0;
            }
-           else if (p < end) {
+           else {
                /* output escaped char (with line breaks) */
+               assert(p < end);
                if (eol_len && linelen > MAX_LINE - 4) {
                    sv_catpvn(RETVAL, "=", 1);
                    sv_catpvn(RETVAL, eol, eol_len);
@@ -347,10 +363,6 @@ encode_qp(sv,...)
                p++;
                linelen += 3;
            }
-           else {
-               assert(p == end);
-               break;
-           }
 
            /* optimize reallocs a bit */
            if (SvLEN(RETVAL) > 80 && SvLEN(RETVAL) - SvCUR(RETVAL) < 3) {
@@ -359,6 +371,11 @@ encode_qp(sv,...)
            }
         }
 
+       if (SvCUR(RETVAL) && eol_len && linelen) {
+           sv_catpvn(RETVAL, "=", 1);
+           sv_catpvn(RETVAL, eol, eol_len);
+       }
+
        OUTPUT:
        RETVAL
 
@@ -398,23 +415,31 @@ decode_qp(sv)
                    }
                    whitespace = 0;
                 }
-               if (*str == '=' && (str + 2) < end && isxdigit(str[1]) && isxdigit(str[2])) {
-                   char buf[3];
-                    str++;
-                   buf[0] = *str++;
-                   buf[1] = *str++;
-                   buf[2] = '\0';
-                   *r++ = (char)strtol(buf, 0, 16);
-               }
-               else if (*str == '=' && (str + 1) < end && str[1] == '\n') {
-                   str += 2;
+               if (*str == '=') {
+                   if ((str + 2) < end && isXDIGIT(str[1]) && isXDIGIT(str[2])) {
+                       char buf[3];
+                        str++;
+                       buf[0] = *str++;
+                       buf[1] = *str++;
+                       buf[2] = '\0';
+                       *r++ = (char)strtol(buf, 0, 16);
+                   }
+                   else {
+                       /* look for soft line break */
+                       char *p = str + 1;
+                       while (p < end && (*p == ' ' || *p == '\t'))
+                           p++;
+                       if (p < end && *p == '\n')
+                           str = p + 1;
+                       else if ((p + 1) < end && *p == '\r' && *(p + 1) == '\n')
+                           str = p + 2;
+                       else
+                           *r++ = *str++; /* give up */
+                   }
                }
-               else if (*str == '=' && (str + 2) < end && str[1] == '\r' && str[2] == '\n') {
-                   str += 3;
+               else {
+                   *r++ = *str++;
                }
-               else {
-                   *r++ = *str++;
-                }
            }
        }
        if (whitespace) {