]> arthur.barton.de Git - bup.git/blobdiff - lib/bup/_helpers.c
txstat: fix for python 3
[bup.git] / lib / bup / _helpers.c
index 30c627fa32c3114b4ed51c48807577afca11d9a3..9078be3da723d8e7757662950a7521bdc275754f 100644 (file)
@@ -15,6 +15,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 #ifdef HAVE_SYS_MMAN_H
 #include <sys/mman.h>
@@ -28,6 +29,9 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
 
 #ifdef HAVE_LINUX_FS_H
 #include <linux/fs.h>
 
 typedef unsigned char byte;
 
-static int istty2 = 0;
+
+typedef struct {
+    int istty2;
+} state_t;
+
+#if PY_MAJOR_VERSION < 3
+static state_t state;
+#  define get_state(x) (&state)
+#  define cstr_argf "s"
+#else
+#  define get_state(x) ((state_t *) PyModule_GetState(x))
+#  define cstr_argf "y"
+#endif // PY_MAJOR_VERSION >= 3
 
 
 #ifndef htonll
@@ -94,6 +110,8 @@ static uint64_t htonll(uint64_t value)
     (((x) >= 0) ? PyLong_FromUnsignedLongLong(x) : PyLong_FromLongLong(x))
 
 
+
+#if PY_MAJOR_VERSION < 3
 static int bup_ulong_from_pyint(unsigned long *x, PyObject *py,
                                 const char *name)
 {
@@ -114,12 +132,15 @@ static int bup_ulong_from_pyint(unsigned long *x, PyObject *py,
     *x = tmp;
     return 1;
 }
+#endif
 
 
 static int bup_ulong_from_py(unsigned long *x, PyObject *py, const char *name)
 {
+#if PY_MAJOR_VERSION < 3
     if (PyInt_Check(py))
         return bup_ulong_from_pyint(x, py, name);
+#endif
 
     if (!PyLong_Check(py))
     {
@@ -158,6 +179,7 @@ static int bup_uint_from_py(unsigned int *x, PyObject *py, const char *name)
 static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py,
                               const char *name)
 {
+#if PY_MAJOR_VERSION < 3
     if (PyInt_Check(py))
     {
         unsigned long tmp;
@@ -168,6 +190,7 @@ static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py,
         }
         return 0;
     }
+#endif
 
     if (!PyLong_Check(py))
     {
@@ -188,6 +211,27 @@ static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py,
 }
 
 
+static PyObject *bup_bytescmp(PyObject *self, PyObject *args)
+{
+    PyObject *py_s1, *py_s2;  // This is really a PyBytes/PyString
+    if (!PyArg_ParseTuple(args, "SS", &py_s1, &py_s2))
+       return NULL;
+    char *s1, *s2;
+    Py_ssize_t s1_len, s2_len;
+    if (PyBytes_AsStringAndSize(py_s1, &s1, &s1_len) == -1)
+        return NULL;
+    if (PyBytes_AsStringAndSize(py_s2, &s2, &s2_len) == -1)
+        return NULL;
+    const Py_ssize_t n = (s1_len < s2_len) ? s1_len : s2_len;
+    const int cmp = memcmp(s1, s2, n);
+    if (cmp != 0)
+        return PyLong_FromLong(cmp);
+    if (s1_len == s2_len)
+        return PyLong_FromLong(0);;
+    return PyLong_FromLong((s1_len < s2_len) ? -1 : 1);
+}
+
+
 // Probably we should use autoconf or something and set HAVE_PY_GETARGCARGV...
 #if __WIN32__ || __CYGWIN__
 
@@ -301,8 +345,7 @@ static PyObject *record_sparse_zeros(unsigned long long *new_pending,
 }
 
 
-static const byte * find_not_zero(const byte * const start,
-                                  const byte * const end)
+static byte* find_not_zero(const byte * const start, const byte * const end)
 {
     // Return a pointer to first non-zero byte between start and end,
     // or end if there isn't one.
@@ -310,37 +353,37 @@ static const byte * find_not_zero(const byte * const start,
     const unsigned char *cur = start;
     while (cur < end && *cur == 0)
         cur++;
-    return cur;
+    return (byte *) cur;
 }
 
 
-static const byte * const find_trailing_zeros(const byte * const start,
-                                              const byte * const end)
+static byte* find_trailing_zeros(const byte * const start,
+                                 const byte * const end)
 {
     // Return a pointer to the start of any trailing run of zeros, or
     // end if there isn't one.
     assert(start <= end);
     if (start == end)
-        return end;
+        return (byte *) end;
     const byte * cur = end;
     while (cur > start && *--cur == 0) {}
     if (*cur == 0)
-        return cur;
+        return (byte *) cur;
     else
-        return cur + 1;
+        return (byte *) (cur + 1);
 }
 
 
-static const byte *find_non_sparse_end(const byte * const start,
-                                       const byte * const end,
-                                       const unsigned long long min_len)
+static byte *find_non_sparse_end(const byte * const start,
+                                 const byte * const end,
+                                 const unsigned long long min_len)
 {
     // Return the first pointer to a min_len sparse block in [start,
     // end) if there is one, otherwise a pointer to the start of any
     // trailing run of zeros.  If there are no trailing zeros, return
     // end.
     if (start == end)
-        return end;
+        return (byte *) end;
     assert(start < end);
     assert(min_len);
     // Probe in min_len jumps, searching backward from the jump
@@ -361,7 +404,7 @@ static const byte *find_non_sparse_end(const byte * const start,
             assert(candidate >= start);
             assert(candidate <= end);
             assert(*candidate == 0);
-            return candidate;
+            return (byte *) candidate;
         }
         else
         {
@@ -371,7 +414,7 @@ static const byte *find_non_sparse_end(const byte * const start,
     }
 
     if (candidate == end)
-        return end;
+        return (byte *) end;
 
     // No min_len sparse run found, search backward from end
     const byte * const trailing_zeros = find_trailing_zeros(end_of_known_zeros,
@@ -383,20 +426,20 @@ static const byte *find_non_sparse_end(const byte * const start,
         assert(candidate < end);
         assert(*candidate == 0);
         assert(end - candidate < min_len);
-        return candidate;
+        return (byte *) candidate;
     }
 
     if (trailing_zeros == end)
     {
         assert(*(end - 1) != 0);
-        return end;
+        return (byte *) end;
     }
 
     assert(end - trailing_zeros < min_len);
     assert(trailing_zeros >= start);
     assert(trailing_zeros < end);
     assert(*trailing_zeros == 0);
-    return trailing_zeros;
+    return (byte *) trailing_zeros;
 }
 
 
@@ -702,6 +745,11 @@ struct sha {
     unsigned char bytes[20];
 };
 
+static inline int _cmp_sha(const struct sha *sha1, const struct sha *sha2)
+{
+    return memcmp(sha1->bytes, sha2->bytes, sizeof(sha1->bytes));
+}
+
 
 struct idx {
     unsigned char *map;
@@ -712,17 +760,6 @@ struct idx {
     int name_base;
 };
 
-
-static int _cmp_sha(const struct sha *sha1, const struct sha *sha2)
-{
-    int i;
-    for (i = 0; i < sizeof(struct sha); i++)
-       if (sha1->bytes[i] != sha2->bytes[i])
-           return sha1->bytes[i] - sha2->bytes[i];
-    return 0;
-}
-
-
 static void _fix_idx_order(struct idx **idxs, int *last_i)
 {
     struct idx *idx;
@@ -821,7 +858,7 @@ static PyObject *merge_into(PyObject *self, PyObject *args)
     {
        struct idx *idx;
        uint32_t new_prefix;
-       if (count % 102424 == 0 && istty2)
+       if (count % 102424 == 0 && get_state(self)->istty2)
            fprintf(stderr, "midx: writing %.2f%% (%d/%d)\r",
                    count*100.0/total, count, total);
        idx = idxs[last_i];
@@ -1138,12 +1175,12 @@ static PyObject *bup_set_linux_file_attr(PyObject *self, PyObject *args)
     // The extents flag can't be removed, so don't (see chattr(1) and chattr.c).
     orig_attr = 0; // Handle int/long mismatch (see above)
     rc = ioctl(fd, FS_IOC_GETFLAGS, &orig_attr);
-    assert(orig_attr <= UINT_MAX);  // Kernel type is actually int
     if (rc == -1)
     {
         close(fd);
         return PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
     }
+    assert(orig_attr <= UINT_MAX);  // Kernel type is actually int
     attr |= ((unsigned int) orig_attr) & FS_EXTENT_FL;
 
     rc = ioctl(fd, FS_IOC_SETFLAGS, &attr);
@@ -1213,7 +1250,7 @@ static PyObject *bup_utimensat(PyObject *self, PyObject *args)
     PyObject *access_py, *modification_py;
     struct timespec ts[2];
 
-    if (!PyArg_ParseTuple(args, "is((Ol)(Ol))i",
+    if (!PyArg_ParseTuple(args, "i" cstr_argf "((Ol)(Ol))i",
                           &fd,
                           &path,
                           &access_py, &(ts[0].tv_nsec),
@@ -1255,7 +1292,7 @@ static int bup_parse_xutimes_args(char **path,
     PyObject *access_py, *modification_py;
     long long access_us, modification_us; // POSIX guarantees tv_usec is signed.
 
-    if (!PyArg_ParseTuple(args, "s((OL)(OL))",
+    if (!PyArg_ParseTuple(args, cstr_argf "((OL)(OL))",
                           path,
                           &access_py, &access_us,
                           &modification_py, &modification_us))
@@ -1371,7 +1408,7 @@ static PyObject *bup_stat(PyObject *self, PyObject *args)
     int rc;
     char *filename;
 
-    if (!PyArg_ParseTuple(args, "s", &filename))
+    if (!PyArg_ParseTuple(args, cstr_argf, &filename))
         return NULL;
 
     struct stat st;
@@ -1387,7 +1424,7 @@ static PyObject *bup_lstat(PyObject *self, PyObject *args)
     int rc;
     char *filename;
 
-    if (!PyArg_ParseTuple(args, "s", &filename))
+    if (!PyArg_ParseTuple(args, cstr_argf, &filename))
         return NULL;
 
     struct stat st;
@@ -1441,43 +1478,59 @@ static PyObject *bup_localtime(PyObject *self, PyObject *args)
 #ifdef BUP_MINCORE_BUF_TYPE
 static PyObject *bup_mincore(PyObject *self, PyObject *args)
 {
-    const char *src;
-    Py_ssize_t src_ssize;
-    Py_buffer dest;
+    Py_buffer src, dest;
     PyObject *py_src_n, *py_src_off, *py_dest_off;
-    if (!PyArg_ParseTuple(args, "s#OOw*O",
-                          &src, &src_ssize, &py_src_n, &py_src_off,
+
+    if (!PyArg_ParseTuple(args, cstr_argf "*OOw*O",
+                          &src, &py_src_n, &py_src_off,
                           &dest, &py_dest_off))
        return NULL;
 
-    unsigned long long src_size, src_n, src_off, dest_size, dest_off;
+    PyObject *result = NULL;
+
+    unsigned long long src_n, src_off, dest_off;
     if (!(bup_ullong_from_py(&src_n, py_src_n, "src_n")
           && bup_ullong_from_py(&src_off, py_src_off, "src_off")
           && bup_ullong_from_py(&dest_off, py_dest_off, "dest_off")))
-        return NULL;
+        goto clean_and_return;
 
-    if (!INTEGRAL_ASSIGNMENT_FITS(&src_size, src_ssize))
-        return PyErr_Format(PyExc_OverflowError, "invalid src size");
     unsigned long long src_region_end;
+    if (!uadd(&src_region_end, src_off, src_n)) {
+        result = PyErr_Format(PyExc_OverflowError, "(src_off + src_n) too large");
+        goto clean_and_return;
+    }
+    if (src_region_end > src.len) {
+        result = PyErr_Format(PyExc_OverflowError, "region runs off end of src");
+        goto clean_and_return;
+    }
 
-    if (!uadd(&src_region_end, src_off, src_n))
-        return PyErr_Format(PyExc_OverflowError, "(src_off + src_n) too large");
-    if (src_region_end > src_size)
-        return PyErr_Format(PyExc_OverflowError, "region runs off end of src");
-
-    if (!INTEGRAL_ASSIGNMENT_FITS(&dest_size, dest.len))
-        return PyErr_Format(PyExc_OverflowError, "invalid dest size");
-    if (dest_off > dest_size)
-        return PyErr_Format(PyExc_OverflowError, "region runs off end of dest");
+    unsigned long long dest_size;
+    if (!INTEGRAL_ASSIGNMENT_FITS(&dest_size, dest.len)) {
+        result = PyErr_Format(PyExc_OverflowError, "invalid dest size");
+        goto clean_and_return;
+    }
+    if (dest_off > dest_size) {
+        result = PyErr_Format(PyExc_OverflowError, "region runs off end of dest");
+        goto clean_and_return;
+    }
 
     size_t length;
-    if (!INTEGRAL_ASSIGNMENT_FITS(&length, src_n))
-        return PyErr_Format(PyExc_OverflowError, "src_n overflows size_t");
-    int rc = mincore((void *)(src + src_off), src_n,
+    if (!INTEGRAL_ASSIGNMENT_FITS(&length, src_n)) {
+        result = PyErr_Format(PyExc_OverflowError, "src_n overflows size_t");
+        goto clean_and_return;
+    }
+    int rc = mincore((void *)(src.buf + src_off), src_n,
                      (BUP_MINCORE_BUF_TYPE *) (dest.buf + dest_off));
-    if (rc != 0)
-        return PyErr_SetFromErrno(PyExc_OSError);
-    return Py_BuildValue("O", Py_None);
+    if (rc != 0) {
+        result = PyErr_SetFromErrno(PyExc_OSError);
+        goto clean_and_return;
+    }
+    result = Py_BuildValue("O", Py_None);
+
+ clean_and_return:
+    PyBuffer_Release(&src);
+    PyBuffer_Release(&dest);
+    return result;
 }
 #endif /* def BUP_MINCORE_BUF_TYPE */
 
@@ -1544,6 +1597,8 @@ static PyMethodDef helper_methods[] = {
     { "localtime", bup_localtime, METH_VARARGS,
       "Return struct_time elements plus the timezone offset and name." },
 #endif
+    { "bytescmp", bup_bytescmp, METH_VARARGS,
+      "Return a negative value if x < y, zero if equal, positive otherwise."},
 #ifdef BUP_MINCORE_BUF_TYPE
     { "mincore", bup_mincore, METH_VARARGS,
       "For mincore(src, src_n, src_off, dest, dest_off)"
@@ -1552,12 +1607,12 @@ static PyMethodDef helper_methods[] = {
     { NULL, NULL, 0, NULL },  // sentinel
 };
 
-
-PyMODINIT_FUNC init_helpers(void)
+static int setup_module(PyObject *m)
 {
-    // FIXME: migrate these tests to configure.  Check against the
-    // type we're going to use when passing to python.  Other stat
-    // types are tested at runtime.
+    // FIXME: migrate these tests to configure, or at least don't
+    // possibly crash the whole application.  Check against the type
+    // we're going to use when passing to python.  Other stat types
+    // are tested at runtime.
     assert(sizeof(ino_t) <= sizeof(unsigned PY_LONG_LONG));
     assert(sizeof(off_t) <= sizeof(PY_LONG_LONG));
     assert(sizeof(blksize_t) <= sizeof(PY_LONG_LONG));
@@ -1577,10 +1632,6 @@ PyMODINIT_FUNC init_helpers(void)
     }
 
     char *e;
-    PyObject *m = Py_InitModule("_helpers", helper_methods);
-    if (m == NULL)
-        return;
-
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wtautological-compare" // For INTEGER_TO_PY().
     {
@@ -1617,6 +1668,52 @@ PyMODINIT_FUNC init_helpers(void)
 #pragma clang diagnostic pop  // ignored "-Wtautological-compare"
 
     e = getenv("BUP_FORCE_TTY");
-    istty2 = isatty(2) || (atoi(e ? e : "0") & 2);
+    get_state(m)->istty2 = isatty(2) || (atoi(e ? e : "0") & 2);
     unpythonize_argv();
+    return 1;
+}
+
+
+#if PY_MAJOR_VERSION < 3
+
+PyMODINIT_FUNC init_helpers(void)
+{
+    PyObject *m = Py_InitModule("_helpers", helper_methods);
+    if (m == NULL)
+        return;
+
+    if (!setup_module(m))
+    {
+        Py_DECREF(m);
+        return;
+    }
+}
+
+# else // PY_MAJOR_VERSION >= 3
+
+static struct PyModuleDef helpers_def = {
+    PyModuleDef_HEAD_INIT,
+    "_helpers",
+    NULL,
+    sizeof(state_t),
+    helper_methods,
+    NULL,
+    NULL, // helpers_traverse,
+    NULL, // helpers_clear,
+    NULL
+};
+
+PyMODINIT_FUNC PyInit__helpers(void)
+{
+    PyObject *module = PyModule_Create(&helpers_def);
+    if (module == NULL)
+        return NULL;
+    if (!setup_module(module))
+    {
+        Py_DECREF(module);
+        return NULL;
+    }
+    return module;
 }
+
+#endif // PY_MAJOR_VERSION >= 3