/* fchksum - python extension for fast file checksumming Copyright (C) 2000-2003 Matthew Mueller This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "fchksum.h" #include "md5.h" #include "cksum.h" #include "sum.h" #include #include #include #include #include #ifndef O_BINARY #define O_BINARY 0 #endif #if SIZEOF_LONG > 4 #define PyLong_FromChksumSizeT PyLong_FromUnsignedLong #elif HAVE_LONG_LONG && SIZEOF_LONG_LONG > 4 #define PyLong_FromChksumSizeT PyLong_FromUnsignedLongLong #else static PyObject *PyLong_FromChksumSizeT(chksum_size_t size){ if (!size.size1) return PyLong_FromUnsignedLong(size.size0); else{ return PyLong_FromDouble(size.size1*((double)ULONG_MAX+1)+size.size);/*icky, but doesn't seem to mess up until you get into many terabyte sized files. Most people will have long long anyway, I hope :)*/ } } #endif /*static PyObject *p_ululToL(PyObject *self, PyObject *args){ PyObject *L0, *L1; if (!PyArg_ParseTuple(args, "OO", &L0,&L1)) return NULL; return ululToL(PyLong_AsUnsignedLong(L0),PyLong_AsUnsignedLong(L1)); }*/ int do_callback(struct CallbackInfo *cbinfo, chksum_size_t size) { if (cbinfo->callback) { cb_time_t now; CB_TIME_GET(now); if (CB_TIME_CMP(now, cbinfo->next, >=)){ /* if now >= next */ PyObject *result; CB_TIME_ADD(now, cbinfo->delay, cbinfo->next); /* next = now + delay */ result = PyObject_CallFunction(cbinfo->callback, "(O)", PyLong_FromChksumSizeT(size)); if (result==NULL) return -1; Py_DECREF(result); } } return 0; } static int parseandopen(PyObject *args,char **fname, struct CallbackInfo *cbinfo){ int fd; float delay=0.1; cbinfo->callback = NULL; if (!PyArg_ParseTuple(args, "s|Of", fname, &cbinfo->callback, &delay)) return -1; if (cbinfo->callback == Py_None) cbinfo->callback = NULL; if (cbinfo->callback) { if (!PyCallable_Check(cbinfo->callback)) { PyErr_SetString(PyExc_TypeError, "2nd parameter must be callable"); return -1; } #ifdef HAVE_GETTIMEOFDAY cbinfo->delay.tv_sec = (long)delay; cbinfo->delay.tv_usec = (long)((delay - (long)delay)*1000000); #else cbinfo->delay = (time_t)delay; if (delay && !cbinfo->delay) cbinfo->delay = 1; /*avoid excessive calling.*/ #endif CB_TIME_GET(cbinfo->next); CB_TIME_ADD(cbinfo->next,cbinfo->delay,cbinfo->next); /* next = now + delay */ } if (**fname=='\0') fd=STDIN_FILENO; else fd=open(*fname,O_RDONLY|O_BINARY); if (fd==-1){ PyErr_SetFromErrnoWithFilename(PyExc_IOError,*fname); return -1; } return fd; } static PyObject *fcrc32_L(PyObject *args,uLong *res){ CallbackInfo cbinfo; char *fname; int fd; chksum_size_t size; char buffer[FCHKSUMBLOCKSIZE]; int sum; /* size_t n;*/ uLong crc = crc32(0L, Z_NULL, 0); if ((fd=parseandopen(args,&fname,&cbinfo))==-1) return NULL; chksum_size_reset(size); while (1) { /*well, for crc32 taking care of partial reads to fully fill the buffer before testing doesn't seem to help much at all.*/ /* Read block. Take care for partial reads. */ /* sum=0; do{ n = fread (buffer + sum, 1, 4096 - sum, f); sum += n; }while (sum < 4096 && n != 0);*/ /* sum = fread (buffer, 1, FCHKSUMBLOCKSIZE, f);*/ sum = read (fd, buffer, FCHKSUMBLOCKSIZE); if (sum <= 0){ if (sum < 0){ /* if (ferror (f)){*/ PyErr_SetFromErrnoWithFilename(PyExc_IOError,fname); close(fd); return NULL; } close(fd); break; } crc = crc32(crc, buffer, sum); chksum_size_add(size, sum); if (do_callback(&cbinfo, size)) { close(fd); return NULL; } } *res=crc; return PyLong_FromChksumSizeT(size); } static char fcrc32__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the crc32 as an int." ; static PyObject *fcrc32(PyObject *self,PyObject *args){ uLong res; PyObject *size; if ((size=fcrc32_L(args,&res))==NULL) return NULL; return Py_BuildValue("(l,N)", res,size); } static char fcrc32d__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the crc32 as a 4 byte string." ; static PyObject *fcrc32d(PyObject *self,PyObject *args){ uLong res; PyObject *size; #if !(WORDS_BIGENDIAN && SIZEOF_LONG==4) unsigned char out[4]; #endif if ((size=fcrc32_L(args,&res))==NULL) return NULL; #if WORDS_BIGENDIAN && SIZEOF_LONG==4 return Py_BuildValue("(s#,N)", &res, 4, size); #else out[0] = res >> 24; out[1] = (res >> 16) & 0xFF; out[2] = (res >> 8) & 0xFF; out[3] = res & 0xFF; return Py_BuildValue("(s#,N)", &out, 4, size); #endif } static char fcrc32t__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the crc32 as a 8 char hex string." ; static PyObject *fcrc32t(PyObject *self,PyObject *args){ uLong res; PyObject *size; unsigned char out[9]; if ((size=fcrc32_L(args,&res))==NULL) return NULL; sprintf(out,"%08lX",res); return Py_BuildValue("(s#,N)", out, 8, size); } static PyObject *fmd5_L(PyObject *args,unsigned char *res){ CallbackInfo cbinfo; char *fname; int fd,r; chksum_size_t size; if ((fd=parseandopen(args,&fname,&cbinfo))==-1) return NULL; if ((r=md5_stream(fd,res,&size,&cbinfo))!=CHKSUM_OK){ if (r==CHKSUM_FERROR) PyErr_SetFromErrnoWithFilename(PyExc_IOError,fname); close(fd); return NULL; } close(fd); return PyLong_FromChksumSizeT(size); } static char fmd5t__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the md5sum as a 32 char hex string." ; static PyObject *fmd5t(PyObject *self,PyObject *args){ unsigned char res[16]; unsigned char out[33]; int i; PyObject *size; if ((size=fmd5_L(args,res))==NULL) return NULL; for (i=0;i<16;i++){ sprintf(out+i*2,"%02x",res[i]); } return Py_BuildValue("(s#,N)", out,32,size); } static char fmd5__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the md5sum as a 16 byte string." ; static PyObject *fmd5(PyObject *self,PyObject *args){ unsigned char res[16]; PyObject *size; if ((size=fmd5_L(args,res))==NULL) return NULL; return Py_BuildValue("(s#,N)", res,16,size); } static PyObject *fcksum_L(PyObject *args,uLong *res){ CallbackInfo cbinfo; char *fname; int fd,r; chksum_size_t size; if ((fd=parseandopen(args,&fname,&cbinfo))==-1) return NULL; if ((r=cksum_stream(fd,res,&size,&cbinfo))!=CHKSUM_OK){ if (r==CHKSUM_FERROR) PyErr_SetFromErrnoWithFilename(PyExc_IOError,fname); close(fd); return NULL; } close(fd); return PyLong_FromChksumSizeT(size); } static char fcksumt__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the cksum as a dec string." ; static PyObject *fcksumt(PyObject *self,PyObject *args){ uLong res; PyObject *size; unsigned char out[11]; if ((size=fcksum_L(args,&res))==NULL) return NULL; sprintf(out,"%lu",res); return Py_BuildValue("(s,N)", out, size); } static char fcksumd__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the cksum as a 4 byte string." ; static PyObject *fcksumd(PyObject *self,PyObject *args){ uLong res; PyObject *size; #if !(WORDS_BIGENDIAN && SIZEOF_LONG==4) unsigned char out[4]; #endif if ((size=fcksum_L(args,&res))==NULL) return NULL; #if WORDS_BIGENDIAN && SIZEOF_LONG==4 return Py_BuildValue("(s#,N)", &res, 4, size); #else out[0] = res >> 24; out[1] = (res >> 16) & 0xFF; out[2] = (res >> 8) & 0xFF; out[3] = res & 0xFF; return Py_BuildValue("(s#,N)", &out, 4, size); #endif } static char fcksum__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the cksum as an int." ; static PyObject *fcksum(PyObject *self,PyObject *args){ uLong res; PyObject *size; if ((size=fcksum_L(args,&res))==NULL) return NULL; /* if res would make a negative pyint, return as a pylong */ /* if ((long)res<0) return Py_BuildValue("(N,N)", PyLong_FromUnsignedLong(res), size);*/ return Py_BuildValue("(l,N)", res,size); } static PyObject *fbsdsum_L(PyObject *args,uLong *res){ CallbackInfo cbinfo; char *fname; int fd,r; chksum_size_t size; if ((fd=parseandopen(args,&fname,&cbinfo))==-1) return NULL; if ((r=bsd_sum_stream(fd,res,&size,&cbinfo))!=CHKSUM_OK){ if (r==CHKSUM_FERROR) PyErr_SetFromErrnoWithFilename(PyExc_IOError,fname); close(fd); return NULL; } close(fd); return PyLong_FromChksumSizeT(size); } static char fbsdsumt__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the bsd-style sum as a 5 char dec string." ; static PyObject *fbsdsumt(PyObject *self,PyObject *args){ uLong res; PyObject *size; unsigned char out[6]; if ((size=fbsdsum_L(args,&res))==NULL) return NULL; sprintf(out,"%05lu",res); return Py_BuildValue("(s#,N)", out, 5, size); } static char fbsdsum__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the bsd-style sum as an int." ; static PyObject *fbsdsum(PyObject *self,PyObject *args){ uLong res; PyObject *size; if ((size=fbsdsum_L(args,&res))==NULL) return NULL; return Py_BuildValue("(l,N)", res,size); } static PyObject *fsysvsum_L(PyObject *args,uLong *res){ CallbackInfo cbinfo; char *fname; int fd,r; chksum_size_t size; if ((fd=parseandopen(args,&fname,&cbinfo))==-1) return NULL; if ((r=sysv_sum_stream(fd,res,&size,&cbinfo))!=CHKSUM_OK){ if (r==CHKSUM_FERROR) PyErr_SetFromErrnoWithFilename(PyExc_IOError,fname); close(fd); return NULL; } close(fd); return PyLong_FromChksumSizeT(size); } static char fsysvsumt__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the sysv-style sum as a dec string." ; static PyObject *fsysvsumt(PyObject *self,PyObject *args){ uLong res; PyObject *size; unsigned char out[6]; if ((size=fsysvsum_L(args,&res))==NULL) return NULL; sprintf(out,"%lu",res); return Py_BuildValue("(s,N)", out, size); } static char fsysvsum__doc__[] = "filename[, callback, cbdelay] -> checksum, size. Return the sysv-style sum as an int." ; static PyObject *fsysvsum(PyObject *self,PyObject *args){ uLong res; PyObject *size; if ((size=fsysvsum_L(args,&res))==NULL) return NULL; return Py_BuildValue("(l,N)", res,size); } static char version__doc__[] = "Return the interface version number.\n\ \n\ v1.0-1.1 had no version() function\n\ v1.2 returned 2\n\ v1.3-1.6 returns 3\n\ v1.7 returns 4\n\ "; static PyObject *version(PyObject *self,PyObject *args){ return Py_BuildValue("i", 4); } static PyMethodDef fchksumMethods[] = { {"fmd5", fmd5, METH_VARARGS, fmd5__doc__}, {"fmd5t", fmd5t, METH_VARARGS, fmd5t__doc__}, {"fcrc32", fcrc32, METH_VARARGS, fcrc32__doc__}, {"fcrc32d", fcrc32d, METH_VARARGS, fcrc32d__doc__}, {"fcrc32t", fcrc32t, METH_VARARGS, fcrc32t__doc__}, {"fcksum", fcksum, METH_VARARGS, fcksum__doc__}, {"fcksumd", fcksumd, METH_VARARGS, fcksumd__doc__}, {"fcksumt", fcksumt, METH_VARARGS, fcksumt__doc__}, {"fbsdsum", fbsdsum, METH_VARARGS, fbsdsum__doc__}, {"fbsdsumt", fbsdsumt, METH_VARARGS, fbsdsumt__doc__}, {"fsysvsum", fsysvsum, METH_VARARGS, fsysvsum__doc__}, {"fsysvsumt", fsysvsumt, METH_VARARGS, fsysvsumt__doc__}, {"version", version, METH_VARARGS, version__doc__}, /* {"ululToL", p_ululToL, METH_VARARGS},*/ {NULL, NULL} /* Sentinel */ }; static char fchksum_module__doc__[] ="\ This module provides quick and easy functions to find checksums of files.\n\ It supports md5, crc32, cksum, bsd-style sum, and sysv-style sum.\n\ The advantage of using fchksum over the python md5 and zlib(.crc32)\n\ modules is both ease of use and speed. You only need to tell it the\n\ filename and the actual work is done by C code. Compared to the\n\ implementing a read loop in python with the standard python modules,\n\ fchksum is up to 2.0x faster in md5 and 1.1x faster in crc32.\n\ \n\ All checksum functions take a filename as a string, and optional callback\n\ function, and callback delay (in seconds), and return a tuple (checksum,\n\ size). An empty string may be substituted for filename to read from\n\ stdin. The returned size is always a python Long, and the checksum\n\ return type varies depending on the function.\n\ "; void initfchksum(void) { (void) Py_InitModule3("fchksum", fchksumMethods, fchksum_module__doc__); }