Sync up with Digest-MD5-2.38 from CPAN
[p5sagit/p5-mst-13.2.git] / ext / Digest / MD5 / MD5.xs
index 76f54cd..a743b05 100644 (file)
@@ -7,7 +7,7 @@
  *  Copyright 1991-1992 RSA Data Security, Inc.
  *
  * This code is derived from Neil Winton's MD5-1.7 Perl module, which in
- * turn is derived from the reference implementation in RFC 1231 which
+ * turn is derived from the reference implementation in RFC 1321 which
  * comes with this message:
  *
  * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
@@ -35,6 +35,7 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+#define PERL_NO_GET_CONTEXT     /* we want efficiency */
 #include "EXTERN.h"
 #include "perl.h"
 #include "XSUB.h"
@@ -42,10 +43,50 @@ extern "C" {
 }
 #endif
 
-#ifndef SvPVbyte
+#ifndef PERL_VERSION
+#    include <patchlevel.h>
+#    if !(defined(PERL_VERSION) || (SUBVERSION > 0 && defined(PATCHLEVEL)))
+#        include <could_not_find_Perl_patchlevel.h>
+#    endif
+#    define PERL_REVISION       5
+#    define PERL_VERSION        PATCHLEVEL
+#    define PERL_SUBVERSION     SUBVERSION
+#endif
+
+#if PERL_VERSION <= 4 && !defined(PL_dowarn)
+   #define PL_dowarn dowarn
+#endif
+
+#ifdef G_WARN_ON
+   #define DOWARN (PL_dowarn & G_WARN_ON)
+#else
+   #define DOWARN PL_dowarn
+#endif
+
+#ifdef SvPVbyte
+   #if PERL_REVISION == 5 && PERL_VERSION < 7
+       /* SvPVbyte does not work in perl-5.6.1, borrowed version for 5.7.3 */
+       #undef SvPVbyte
+       #define SvPVbyte(sv, lp) \
+         ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \
+          ? ((lp = SvCUR(sv)), SvPVX(sv)) : my_sv_2pvbyte(aTHX_ sv, &lp))
+
+       static char *
+       my_sv_2pvbyte(pTHX_ register SV *sv, STRLEN *lp)
+       {
+          sv_utf8_downgrade(sv,0);
+           return SvPV(sv,*lp);
+       }
+   #endif
+#else
    #define SvPVbyte SvPV
 #endif
 
+#ifndef dTHX
+   #define pTHX_
+   #define aTHX_
+#endif
+
 /* Perl does not guarantee that U32 is exactly 32 bits.  Some system
  * has no integral type with exactly 32 bits.  For instance, A Cray has
  * short, int and long all at 64 bits so we need to apply this macro
@@ -80,10 +121,10 @@ extern "C" {
 #ifndef BYTESWAP
 static void u2s(U32 u, U8* s)
 {
-    *s++ = u         & 0xFF;
-    *s++ = (u >>  8) & 0xFF;
-    *s++ = (u >> 16) & 0xFF;
-    *s   = (u >> 24) & 0xFF;
+    *s++ = (U8)(u         & 0xFF);
+    *s++ = (U8)((u >>  8) & 0xFF);
+    *s++ = (U8)((u >> 16) & 0xFF);
+    *s   = (U8)((u >> 24) & 0xFF);
 }
 
 #define s2u(s,u) ((u) =  (U32)(*s)            |  \
@@ -110,7 +151,7 @@ typedef struct {
  * padding is also the reason the buffer in MD5_CTX have to be
  * 128 bytes.
  */
-static unsigned char PADDING[64] = {
+static const unsigned char PADDING[64] = {
   0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@@ -423,7 +464,7 @@ MD5Final(U8* digest, MD5_CTX *ctx)
 #define INT2PTR(any,d) (any)(d)
 #endif
 
-static MD5_CTX* get_md5_ctx(SV* sv)
+static MD5_CTX* get_md5_ctx(pTHX_ SV* sv)
 {
     if (SvROK(sv)) {
        sv = SvRV(sv);
@@ -441,7 +482,7 @@ static MD5_CTX* get_md5_ctx(SV* sv)
 
 static char* hex_16(const unsigned char* from, char* to)
 {
-    static char *hexdigits = "0123456789abcdef";
+    static const char hexdigits[] = "0123456789abcdef";
     const unsigned char *end = from + 16;
     char *d = to;
 
@@ -456,7 +497,7 @@ static char* hex_16(const unsigned char* from, char* to)
 
 static char* base64_16(const unsigned char* from, char* to)
 {
-    static char* base64 =
+    static const char base64[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     const unsigned char *end = from + 16;
     unsigned char c1, c2, c3;
@@ -484,7 +525,7 @@ static char* base64_16(const unsigned char* from, char* to)
 #define F_HEX 1
 #define F_B64 2
 
-static SV* make_mortal_sv(const unsigned char *src, int type)
+static SV* make_mortal_sv(pTHX_ const unsigned char *src, int type)
 {
     STRLEN len;
     char result[33];
@@ -534,12 +575,27 @@ new(xclass)
            sv_setref_pv(ST(0), sclass, (void*)context);
            SvREADONLY_on(SvRV(ST(0)));
        } else {
-           context = get_md5_ctx(xclass);
+           context = get_md5_ctx(aTHX_ xclass);
        }
         MD5Init(context);
        XSRETURN(1);
 
 void
+clone(self)
+       SV* self
+    PREINIT:
+       MD5_CTX* cont = get_md5_ctx(aTHX_ self);
+       const char *myname = sv_reftype(SvRV(self),TRUE);
+       MD5_CTX* context;
+    PPCODE:
+       New(55, context, 1, MD5_CTX);
+       ST(0) = sv_newmortal();
+       sv_setref_pv(ST(0), myname , (void*)context);
+       SvREADONLY_on(SvRV(ST(0)));
+       memcpy(context,cont,sizeof(MD5_CTX));
+       XSRETURN(1);
+
+void
 DESTROY(context)
        MD5_CTX* context
     CODE:
@@ -549,7 +605,7 @@ void
 add(self, ...)
        SV* self
     PREINIT:
-       MD5_CTX* context = get_md5_ctx(self);
+       MD5_CTX* context = get_md5_ctx(aTHX_ self);
        int i;
        unsigned char *data;
        STRLEN len;
@@ -565,28 +621,45 @@ addfile(self, fh)
        SV* self
        InputStream fh
     PREINIT:
-       MD5_CTX* context = get_md5_ctx(self);
+       MD5_CTX* context = get_md5_ctx(aTHX_ self);
        STRLEN fill = context->bytes_low & 0x3F;
+#ifdef USE_HEAP_INSTEAD_OF_STACK
+       unsigned char* buffer;
+#else
        unsigned char buffer[4096];
+#endif
        int  n;
     CODE:
        if (fh) {
+#ifdef USE_HEAP_INSTEAD_OF_STACK
+           New(0, buffer, 4096, unsigned char);
+           assert(buffer);
+#endif
             if (fill) {
                /* The MD5Update() function is faster if it can work with
                 * complete blocks.  This will fill up any buffered block
                 * first.
                 */
                STRLEN missing = 64 - fill;
-               if ( (n = PerlIO_read(fh, buffer, missing)))
+               if ( (n = PerlIO_read(fh, buffer, missing)) > 0)
                    MD5Update(context, buffer, n);
                else
                    XSRETURN(1);  /* self */
            }
 
-           /* Process blocks until EOF */
-            while ( (n = PerlIO_read(fh, buffer, sizeof(buffer)))) {
+           /* Process blocks until EOF or error */
+            while ( (n = PerlIO_read(fh, buffer, sizeof(buffer))) > 0) {
                MD5Update(context, buffer, n);
            }
+#ifdef USE_HEAP_INSTEAD_OF_STACK
+           Safefree(buffer);
+#endif
+           if (PerlIO_error(fh)) {
+               croak("Reading from filehandle failed");
+           }
+       }
+       else {
+           croak("No filehandle passed");
        }
        XSRETURN(1);  /* self */
 
@@ -602,7 +675,7 @@ digest(context)
     PPCODE:
         MD5Final(digeststr, context);
        MD5Init(context);  /* In case it is reused */
-        ST(0) = make_mortal_sv(digeststr, ix);
+        ST(0) = make_mortal_sv(aTHX_ digeststr, ix);
         XSRETURN(1);
 
 void
@@ -619,18 +692,40 @@ md5(...)
        unsigned char digeststr[16];
     PPCODE:
        MD5Init(&ctx);
-       if (PL_dowarn && items > 1) {
-           data = (unsigned char *)SvPVbyte(ST(0), len);
-           if (len == 11 && memEQ("Digest::MD5", data, 11)) {
-                char *f = (ix == F_BIN) ? "md5" :
-                           (ix == F_HEX) ? "md5_hex" : "md5_base64";
-                warn("&Digest::MD5::%s function probably called as method", f);
-            }
+
+       if (DOWARN) {
+            char *msg = 0;
+           if (items == 1) {
+               if (SvROK(ST(0))) {
+                    SV* sv = SvRV(ST(0));
+                   if (SvOBJECT(sv) && strEQ(HvNAME(SvSTASH(sv)), "Digest::MD5"))
+                       msg = "probably called as method";
+                   else
+                       msg = "called with reference argument";
+               }
+           }
+           else if (items > 1) {
+               data = (unsigned char *)SvPVbyte(ST(0), len);
+               if (len == 11 && memEQ("Digest::MD5", data, 11)) {
+                   msg = "probably called as class method";
+               }
+               else if (SvROK(ST(0))) {
+                   SV* sv = SvRV(ST(0));
+                   if (SvOBJECT(sv) && strEQ(HvNAME(SvSTASH(sv)), "Digest::MD5"))
+                       msg = "probably called as method";
+               }
+           }
+           if (msg) {
+               const char *f = (ix == F_BIN) ? "md5" :
+                               (ix == F_HEX) ? "md5_hex" : "md5_base64";
+               warn("&Digest::MD5::%s function %s", f, msg);
+           }
        }
+
        for (i = 0; i < items; i++) {
            data = (unsigned char *)(SvPVbyte(ST(i), len));
            MD5Update(&ctx, data, len);
        }
        MD5Final(digeststr, &ctx);
-        ST(0) = make_mortal_sv(digeststr, ix);
+        ST(0) = make_mortal_sv(aTHX_ digeststr, ix);
         XSRETURN(1);