diff --git a/Makefile.am b/Makefile.am index d2451b8..5ab1893 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,7 +41,7 @@ endif make_SOURCES = ar.c arscan.c commands.c default.c dir.c expand.c file.c \ function.c getopt.c getopt1.c guile.c implicit.c job.c load.c \ - loadapi.c main.c misc.c output.c read.c remake.c rule.c \ + loadapi.c main.c md5.c misc.c output.c read.c remake.c rule.c \ signame.c strcache.c variable.c version.c vpath.c hash.c \ $(remote) diff --git a/Makefile.ami b/Makefile.ami index 7c561a3..07acc6b 100644 --- a/Makefile.ami +++ b/Makefile.ami @@ -119,10 +119,10 @@ CTAGS = ctags -w #guile = guile.o -objs = commands.o job.o dir.o file.o misc.o main.o read.o remake.o \ - rule.o implicit.o default.o variable.o expand.o function.o \ - vpath.o version.o ar.o arscan.o signame.o strcache.o hash.o \ - remote-$(REMOTE).o $(GETOPT) $(ALLOCA) $(extras) $(guile) +objs = commands.o job.o dir.o file.o misc.o main.o read.o remake.o \ + rule.o implicit.o default.o variable.o expand.o function.o \ + vpath.o version.o ar.o arscan.o signame.o strcache.o hash.o \ + md5.o remote-$(REMOTE).o $(GETOPT) $(ALLOCA) $(extras) $(guile) srcs = $(srcdir)commands.c $(srcdir)job.c $(srcdir)dir.c \ $(srcdir)file.c $(srcdir)getloadavg.c $(srcdir)misc.c \ @@ -130,7 +130,7 @@ srcs = $(srcdir)commands.c $(srcdir)job.c $(srcdir)dir.c \ $(srcdir)rule.c $(srcdir)implicit.c $(srcdir)default.c \ $(srcdir)variable.c $(srcdir)expand.c $(srcdir)function.c \ $(srcdir)vpath.c $(srcdir)version.c $(srcdir)hash.c \ - $(srcdir)guile.c $(srcdir)remote-$(REMOTE).c \ + $(srcdir)guile.c $(srcdir)md5.c $(srcdir)remote-$(REMOTE).c \ $(srcdir)ar.c $(srcdir)arscan.c $(srcdir)strcache.c \ $(srcdir)signame.c $(srcdir)signame.h $(GETOPT_SRC) \ $(srcdir)commands.h $(srcdir)dep.h $(srcdir)filedep.h \ @@ -306,3 +306,4 @@ getopt.o: getopt.c getopt1.o : getopt1.c getopt.h getloadavg.o: getloadavg.c amiga.o: amiga.c makeint.h variable.h amiga.h +md5.o: md5.c md5.h diff --git a/SMakefile.template b/SMakefile.template index 4af89ae..2c04c97 100644 --- a/SMakefile.template +++ b/SMakefile.template @@ -128,7 +128,7 @@ CTAGS = ctags -w objs = commands.o job.o dir.o file.o misc.o main.o read.o remake.o \ rule.o implicit.o default.o variable.o expand.o function.o \ vpath.o version.o ar.o arscan.o signame.o strcache.o hash.o \ - output.o remote-$(REMOTE).o $(GLOB) $(GETOPT) $(ALLOCA) \ + output.o md5.o remote-$(REMOTE).o $(GLOB) $(GETOPT) $(ALLOCA) \ $(extras) $(guile) srcs = $(srcdir)commands.c $(srcdir)job.c $(srcdir)dir.c \ @@ -142,7 +142,7 @@ srcs = $(srcdir)commands.c $(srcdir)job.c $(srcdir)dir.c \ $(srcdir)signame.c $(srcdir)signame.h $(GETOPT_SRC) \ $(srcdir)commands.h $(srcdir)dep.h $(srcdir)file.h \ $(srcdir)job.h $(srcdir)makeint.h $(srcdir)rule.h \ - $(srcdir)output.c $(srcdir)output.h \ + $(srcdir)output.c $(srcdir)output.h $(srcdir)md5.c \ $(srcdir)variable.h $(ALLOCA_SRC) $(srcdir)config.h.in diff --git a/doc/make.texi b/doc/make.texi index b0f5af7..19a2b85 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -6991,6 +6991,23 @@ Replacing the variable reference @address@hidden(needs_made)}} with the function call @address@hidden(strip $(needs_made))}} in the @code{ifneq} directive would make it more address@hidden + address@hidden $(md5 @var{string}) address@hidden md5 address@hidden checksum address@hidden md5sum address@hidden hash address@hidden md5 +Computes the MD5 sum of the words in @var{string}. + +The function @code{md5} can be useful to generate filenames. + address@hidden +SRC=file.c load.c +$(md5 ${SRC}): ${SRC} + touch $@@ address@hidden example + @item $(findstring @var{find},@var{in}) @findex findstring @cindex searching for strings @@ -11798,6 +11815,10 @@ Replace words matching @var{pattern} with @var{replacement} in @address@hidden Remove excess whitespace characters from @address@hidden @xref{Text Functions, , Functions for String Substitution and Analysis}. address@hidden $(md5 @var{string}) +compute the md5 checksums in @address@hidden address@hidden Functions, , Functions for String Substitution and Analysis}. + @item $(findstring @var{find},@var{text}) Locate @var{find} in @address@hidden @xref{Text Functions, , Functions for String Substitution and Analysis}. diff --git a/function.c b/function.c index 169c3a1..850cbab 100644 --- a/function.c +++ b/function.c @@ -21,6 +21,7 @@ this program. If not, see . */ #include "job.h" #include "commands.h" #include "debug.h" +#include "md5.h" #ifdef _AMIGA #include "amiga.h" @@ -452,6 +453,33 @@ func_join (char *o, char **argv, const char *funcname UNUSED) static char * +func_md5 (char *o, char **argv, const char *funcname UNUSED) +{ + const char *list_iterator = argv[0]; + unsigned char digest[16]; + const char *p; + unsigned int i,len, n=0; + CMD5* md5ptr = CMD5New(); + + while ((p = find_next_token (&list_iterator, &len)) != 0) + { + if( n > 0 ) CMD5Update(md5ptr," ",1); + CMD5Update(md5ptr,p,len); + ++n; + } + CMD5Finalize(md5ptr,digest); + for(i=0;i< 16;++i) + { + char tmp[3]; + sprintf(tmp, "%02x", digest[i]); + o = variable_buffer_output (o, tmp,2); + } + + free(md5ptr); + return o; +} + +static char * func_origin (char *o, char **argv, const char *funcname UNUSED) { /* Expand the argument. */ @@ -2281,6 +2309,7 @@ static struct function_table_entry function_table_init[] = FT_ENTRY ("flavor", 0, 1, 1, func_flavor), FT_ENTRY ("join", 2, 2, 1, func_join), FT_ENTRY ("lastword", 0, 1, 1, func_lastword), + FT_ENTRY ("md5", 0, 1, 1, func_md5), FT_ENTRY ("patsubst", 3, 3, 1, func_patsubst), FT_ENTRY ("realpath", 0, 1, 1, func_realpath), FT_ENTRY ("shell", 0, 1, 1, func_shell), diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..0790ee7 --- /dev/null +++ b/md5.c @@ -0,0 +1,271 @@ +/* MD5 for GNU make + +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . + +Orignal C++ code from Aaron Ucko / NCBI(National Center for Biotechnology Information) /Public Domain +Converted back to C by Pierre Lindenbaum + +*/ +#include +#include +#include +#include +#include "makeint.h" +#include "md5.h" + + + +#ifdef __BIG_ENDIAN__ +static void s_ByteReverse(unsigned char* buf, size_t longs) + { + uint32_t t; + do { + t = (uint32_t ) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *((uint32_t*)(buf)) = t; + buf += 4; + } while (--longs); + } +#endif + + + +// The four core functions - F1 is optimized somewhat + +// #define F1(x, y, z) (x & y | ~x & z) +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) ((z & x) | (~z & y)) +#define F3(x, y, z) (x ^ (y ^ z)) +#define F4(x, y, z) (y ^ (x | ~z)) + +// This is the central step in the MD5 algorithm. +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +// The core of the MD5 algorithm, this alters an existing MD5 hash to +// reflect the addition of 16 longwords of new data. MD5Update blocks +// the data and converts bytes into longwords for this routine. +static +void CMD5Transform(CMD5* ptr) +{ + uint32_t a, b, c, d; + uint32_t* inw = (uint32_t*)(ptr->m_In); + + a = ptr->m_Buf[0]; + b = ptr->m_Buf[1]; + c = ptr->m_Buf[2]; + d = ptr->m_Buf[3]; + + MD5STEP(F1, a, b, c, d, inw[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, inw[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, inw[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, inw[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, inw[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, inw[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, inw[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, inw[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, inw[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, inw[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, inw[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, inw[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, inw[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, inw[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, inw[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, inw[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, inw[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, inw[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, inw[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, inw[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, inw[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, inw[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, inw[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, inw[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, inw[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, inw[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, inw[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, inw[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, inw[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, inw[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, inw[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, inw[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, inw[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, inw[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, inw[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, inw[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, inw[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, inw[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, inw[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, inw[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, inw[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, inw[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, inw[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, inw[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, inw[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, inw[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, inw[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, inw[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, inw[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, inw[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, inw[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, inw[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, inw[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, inw[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, inw[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, inw[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, inw[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, inw[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, inw[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, inw[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, inw[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, inw[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, inw[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, inw[9] + 0xeb86d391, 21); + + ptr->m_Buf[0] += a; + ptr->m_Buf[1] += b; + ptr->m_Buf[2] += c; + ptr->m_Buf[3] += d; +} + + + +// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious +// initialization constants. +CMD5* CMD5New(void) +{ + CMD5* p = (CMD5*)xmalloc(sizeof(CMD5)); + p->m_Bits = 0; + p->m_Buf[0] = 0x67452301; + p->m_Buf[1] = 0xefcdab89; + p->m_Buf[2] = 0x98badcfe; + p->m_Buf[3] = 0x10325476; + p->m_Finalized = 0; + return p; +} + + +// Update state to reflect the concatenation of another buffer full of bytes. +void CMD5Update(CMD5* ptr,const char* buf, size_t length) +{ + unsigned int tmp; + if ( ptr->m_Finalized ) + { + O (fatal, NILF, _("attempt to update a finalized MD5 instance")); + } + + // Number of leftover bytes in m_In + tmp = (unsigned int)((ptr->m_Bits >> 3) % (sizeof(ptr->m_In)) ); + + // Update bit count + ptr->m_Bits += length << 3; + + // Handle any leading odd-sized chunks + if ( tmp ) + { + unsigned char* p = ptr->m_In + tmp; + + tmp = kBlockSize - tmp; + if (length < tmp) + { + memcpy(p, buf, length); + return; + } + memcpy(p, buf, tmp); + #ifdef __BIG_ENDIAN__ + s_ByteReverse(ptr->m_In, 16); + #endif + CMD5Transform(ptr); + buf += tmp; + length -= tmp; + } + + // Process remaining data in kBlockSize-byte chunks + while (length >= kBlockSize) + { + memcpy(ptr->m_In, buf, kBlockSize); + #ifdef __BIG_ENDIAN__ + s_ByteReversep->(ptr->m_In, 16); + #endif + CMD5Transform(ptr); + buf += kBlockSize; + length -= kBlockSize; + } + + // Handle any remaining bytes of data + memcpy(ptr->m_In, buf, length); +} + + +// Final wrapup - pad to kBlockSize-byte boundary with the bit pattern +// 1 0* (64-bit count of bits processed, MSB-first). +void CMD5Finalize(CMD5* ptr,unsigned char digest[16]) +{ + int count; + unsigned char *p; + if ( ptr->m_Finalized ) + { + memcpy(digest, ptr->m_Buf, 16); + return; + } + + // Compute number of bytes mod kBlockSize + count = (int)((ptr->m_Bits >> 3) % kBlockSize); + + // Set the first char of padding to 0x80. This is safe since there is + // always at least one byte free. + p = ptr->m_In + count; + *p++ = 0x80; + + // Bytes of padding needed to make kBlockSize bytes + count = kBlockSize - 1 - count; + + // Pad out to 56 mod kBlockSize + if (count < 8) + { + // Two lots of padding: Pad the first block to kBlockSize bytes + memset(p, 0, count); + #ifdef __BIG_ENDIAN__ + s_ByteReverse(ptr->m_In, 16); + #endif + CMD5Transform(ptr); + + // Now fill the next block with 56 bytes + memset(ptr->m_In, 0, kBlockSize - 8); + } + else + { + // Pad block to 56 bytes + memset(p, 0, count - 8); + #ifdef __BIG_ENDIAN__ + s_ByteReverse(ptr->m_In, 14); + #endif + } + + // Append length in bits and transform + ((uint32_t*)ptr->m_In)[14] = (uint32_t)(ptr->m_Bits); + ((uint32_t*)ptr->m_In)[15] = (uint32_t)(ptr->m_Bits >> 32); + + CMD5Transform(ptr); + #ifdef __BIG_ENDIAN__ + s_ByteReverse((unsigned char*)(ptr->m_Buf), 4); + #endif + memcpy(digest, ptr->m_Buf, 16); + memset(ptr->m_In, 0, kBlockSize); // may be sensitive + ptr->m_Finalized = 1; + } + diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..5046e1f --- /dev/null +++ b/md5.h @@ -0,0 +1,41 @@ +/* MD5 for GNU make +Copyright (C) 2013-2015 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . */ + +#ifndef MAKE_MD5_H +#define MAKE_MD5_H +#include + + + // Block size defined by algorithm; +#define kBlockSize ((int)64) + +/* struct for computing Message Digest version 5 checksums */ +typedef struct CMD5_t + { + uint32_t m_Buf[4]; + int64_t m_Bits; // must be a 64-bit count + unsigned char m_In[kBlockSize]; + int m_Finalized; + } CMD5; + +/* create a new CMD5 */ +CMD5* CMD5New(void); +/* Update state to reflect the concatenation of another buffer full of bytes. */ +void CMD5Update(CMD5* ptr,const char* buf, size_t length); +/* // Final wrapup - pad to kBlockSize-byte boundary with the bit pattern */ +void CMD5Finalize(CMD5* ptr,unsigned char digest[16]); + +#endif diff --git a/tests/scripts/functions/md5 b/tests/scripts/functions/md5 new file mode 100644 index 0000000..db13db8 --- /dev/null +++ b/tests/scripts/functions/md5 @@ -0,0 +1,44 @@ +$description = "The following test creates a makefile to test the md5 " + ."function."; + +$details = ""; + +# IF YOU NEED >1 MAKEFILE FOR THIS TEST, USE &get_tmpfile; TO GET +# THE NAME OF THE MAKEFILE. THIS INSURES CONSISTENCY AND KEEPS TRACK OF +# HOW MANY MAKEFILES EXIST FOR EASY DELETION AT THE END. +# EXAMPLE: $makefile2 = &get_tmpfile; + + +open(MAKEFILE,"> $makefile"); + +# The Contents of the MAKEFILE ... + +print MAKEFILE "string := \$(md5 A 12345) \n" + ."all: \n" + ."address@hidden \$(string) \n"; + +# END of Contents of MAKEFILE + +close(MAKEFILE); + +&run_make_with_options($makefile,"",&get_logfile,0); + +# Create the answer to what should be produced by this Makefile +$answer = "2cbf53c02fc13ff6f3dfc4621c2e2455\n"; + +# COMPARE RESULTS + +# In this call to compare output, you should use the call &get_logfile(1) +# to send the name of the last logfile created. You may also use +# the special call &get_logfile(1) which returns the same as &get_logfile(1). + +&compare_output($answer,&get_logfile(1)); + +# This tells the test driver that the perl test script executed properly. +1; + + + + + +