#include <time.h>
#endif
+#if defined(BUP_RL_EXPECTED_XOPEN_SOURCE) \
+ && (!defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < BUP_RL_EXPECTED_XOPEN_SOURCE)
+# warning "_XOPEN_SOURCE version is incorrect for readline"
+#endif
+
+#ifdef BUP_HAVE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
#include "bupsplit.h"
#if defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS)
return result;
}
+
static long getpw_buf_size;
static PyObject *pwd_struct_to_py(const struct passwd *pwd, int rc)
}
if (rc == 0)
return Py_BuildValue("O", Py_None);
+ errno = rc;
if (rc == EIO || rc == EMFILE || rc == ENFILE)
return PyErr_SetFromErrno(PyExc_IOError);
- if (rc < 0)
- return PyErr_SetFromErrno(PyExc_OSError);
- assert (0);
+ return PyErr_SetFromErrno(PyExc_OSError);
}
static PyObject *bup_getgrgid(PyObject *self, PyObject *args)
return PyBytes_FromString(buf);
}
+
+#ifdef BUP_HAVE_READLINE
+
+static char *cstr_from_bytes(PyObject *bytes)
+{
+ char *buf;
+ Py_ssize_t length;
+ int rc = PyBytes_AsStringAndSize(bytes, &buf, &length);
+ if (rc == -1)
+ return NULL;
+ char *result = checked_malloc(length, sizeof(char));
+ if (!result)
+ return NULL;
+ memcpy(result, buf, length);
+ return result;
+}
+
+static char **cstrs_from_seq(PyObject *seq)
+{
+ char **result = NULL;
+ seq = PySequence_Fast(seq, "Cannot convert sequence items to C strings");
+ if (!seq)
+ return NULL;
+
+ const Py_ssize_t len = PySequence_Fast_GET_SIZE(seq);
+ if (len > PY_SSIZE_T_MAX - 1) {
+ PyErr_Format(PyExc_OverflowError,
+ "Sequence length %zd too large for conversion to C array",
+ len);
+ goto finish;
+ }
+ result = checked_malloc(len + 1, sizeof(char *));
+ if (!result)
+ goto finish;
+ Py_ssize_t i = 0;
+ for (i = 0; i < len; i++)
+ {
+ PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
+ if (!item)
+ goto abandon_result;
+ result[i] = cstr_from_bytes(item);
+ if (!result[i]) {
+ i--;
+ goto abandon_result;
+ }
+ }
+ result[len] = NULL;
+ goto finish;
+
+ abandon_result:
+ if (result) {
+ for (; i > 0; i--)
+ free(result[i]);
+ free(result);
+ result = NULL;
+ }
+ finish:
+ Py_DECREF(seq);
+ return result;
+}
+
+static char* our_word_break_chars = NULL;
+
+static PyObject *
+bup_set_completer_word_break_characters(PyObject *self, PyObject *args)
+{
+ char *bytes;
+ if (!PyArg_ParseTuple(args, cstr_argf, &bytes))
+ return NULL;
+ char *prev = our_word_break_chars;
+ char *next = strdup(bytes);
+ if (!next)
+ return PyErr_NoMemory();
+ our_word_break_chars = next;
+ rl_completer_word_break_characters = next;
+ if (prev)
+ free(prev);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+bup_get_completer_word_break_characters(PyObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ return PyBytes_FromString(rl_completer_word_break_characters);
+}
+
+static PyObject *bup_get_line_buffer(PyObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ return PyBytes_FromString(rl_line_buffer);
+}
+
+static PyObject *
+bup_parse_and_bind(PyObject *self, PyObject *args)
+{
+ char *bytes;
+ if (!PyArg_ParseTuple(args, cstr_argf ":parse_and_bind", &bytes))
+ return NULL;
+ char *tmp = strdup(bytes); // Because it may modify the arg
+ if (!tmp)
+ return PyErr_NoMemory();
+ int rc = rl_parse_and_bind(tmp);
+ free(tmp);
+ if (rc != 0)
+ return PyErr_Format(PyExc_OSError,
+ "system rl_parse_and_bind failed (%d)", rc);
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *py_on_attempted_completion;
+static char **prev_completions;
+
+static char **on_attempted_completion(const char *text, int start, int end)
+{
+ if (!py_on_attempted_completion)
+ return NULL;
+
+ char **result = NULL;
+ PyObject *py_result = PyObject_CallFunction(py_on_attempted_completion,
+ cstr_argf "ii",
+ text, start, end);
+ if (!py_result)
+ return NULL;
+ if (py_result != Py_None) {
+ result = cstrs_from_seq(py_result);
+ free(prev_completions);
+ prev_completions = result;
+ }
+ Py_DECREF(py_result);
+ return result;
+}
+
+static PyObject *
+bup_set_attempted_completion_function(PyObject *self, PyObject *args)
+{
+ PyObject *completer;
+ if (!PyArg_ParseTuple(args, "O", &completer))
+ return NULL;
+
+ PyObject *prev = py_on_attempted_completion;
+ if (completer == Py_None)
+ {
+ py_on_attempted_completion = NULL;
+ rl_attempted_completion_function = NULL;
+ } else {
+ py_on_attempted_completion = completer;
+ rl_attempted_completion_function = on_attempted_completion;
+ Py_INCREF(completer);
+ }
+ Py_XDECREF(prev);
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *py_on_completion_entry;
+
+static char *on_completion_entry(const char *text, int state)
+{
+ if (!py_on_completion_entry)
+ return NULL;
+
+ PyObject *py_result = PyObject_CallFunction(py_on_completion_entry,
+ cstr_argf "i", text, state);
+ if (!py_result)
+ return NULL;
+ char *result = (py_result == Py_None) ? NULL : cstr_from_bytes(py_result);
+ Py_DECREF(py_result);
+ return result;
+}
+
+static PyObject *
+bup_set_completion_entry_function(PyObject *self, PyObject *args)
+{
+ PyObject *completer;
+ if (!PyArg_ParseTuple(args, "O", &completer))
+ return NULL;
+
+ PyObject *prev = py_on_completion_entry;
+ if (completer == Py_None) {
+ py_on_completion_entry = NULL;
+ rl_completion_entry_function = NULL;
+ } else {
+ py_on_completion_entry = completer;
+ rl_completion_entry_function = on_completion_entry;
+ Py_INCREF(completer);
+ }
+ Py_XDECREF(prev);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+bup_readline(PyObject *self, PyObject *args)
+{
+ char *prompt;
+ if (!PyArg_ParseTuple(args, cstr_argf, &prompt))
+ return NULL;
+ char *line = readline(prompt);
+ if (!line)
+ return PyErr_Format(PyExc_EOFError, "readline EOF");
+ PyObject *result = PyBytes_FromString(line);
+ free(line);
+ return result;
+}
+
+#endif // defined BUP_HAVE_READLINE
+
+#if defined(HAVE_SYS_ACL_H) && \
+ defined(HAVE_ACL_LIBACL_H) && \
+ defined(HAVE_ACL_EXTENDED_FILE) && \
+ defined(HAVE_ACL_GET_FILE) && \
+ defined(HAVE_ACL_TO_ANY_TEXT) && \
+ defined(HAVE_ACL_FROM_TEXT) && \
+ defined(HAVE_ACL_SET_FILE)
+#define ACL_SUPPORT 1
+#include <sys/acl.h>
+#include <acl/libacl.h>
+
+// Returns
+// 0 for success
+// -1 for errors, with python exception set
+// -2 for ignored errors (not supported)
+static int bup_read_acl_to_text(const char *name, acl_type_t type,
+ char **txt, char **num)
+{
+ acl_t acl;
+
+ acl = acl_get_file(name, type);
+ if (!acl) {
+ if (errno == EOPNOTSUPP || errno == ENOSYS)
+ return -2;
+ PyErr_SetFromErrno(PyExc_IOError);
+ return -1;
+ }
+
+ *num = NULL;
+ *txt = acl_to_any_text(acl, "", '\n', TEXT_ABBREVIATE);
+ if (*txt)
+ *num = acl_to_any_text(acl, "", '\n', TEXT_ABBREVIATE | TEXT_NUMERIC_IDS);
+
+ if (*txt && *num)
+ return 0;
+
+ if (errno == ENOMEM)
+ PyErr_NoMemory();
+ else
+ PyErr_SetFromErrno(PyExc_IOError);
+
+ if (*txt)
+ acl_free((acl_t)*txt);
+ if (*num)
+ acl_free((acl_t)*num);
+
+ return -1;
+}
+
+static PyObject *bup_read_acl(PyObject *self, PyObject *args)
+{
+ char *name;
+ int isdir, rv;
+ PyObject *ret = NULL;
+ char *acl_txt = NULL, *acl_num = NULL;
+
+ if (!PyArg_ParseTuple(args, cstr_argf "i", &name, &isdir))
+ return NULL;
+
+ if (!acl_extended_file(name))
+ Py_RETURN_NONE;
+
+ rv = bup_read_acl_to_text(name, ACL_TYPE_ACCESS, &acl_txt, &acl_num);
+ if (rv)
+ goto out;
+
+ if (isdir) {
+ char *def_txt = NULL, *def_num = NULL;
+
+ rv = bup_read_acl_to_text(name, ACL_TYPE_DEFAULT, &def_txt, &def_num);
+ if (rv)
+ goto out;
+
+ ret = Py_BuildValue("[" cstr_argf cstr_argf cstr_argf cstr_argf "]",
+ acl_txt, acl_num, def_txt, def_num);
+
+ if (def_txt)
+ acl_free((acl_t)def_txt);
+ if (def_num)
+ acl_free((acl_t)def_num);
+ } else {
+ ret = Py_BuildValue("[" cstr_argf cstr_argf "]",
+ acl_txt, acl_num);
+ }
+
+out:
+ if (acl_txt)
+ acl_free((acl_t)acl_txt);
+ if (acl_num)
+ acl_free((acl_t)acl_num);
+ if (rv == -2)
+ Py_RETURN_NONE;
+ return ret;
+}
+
+static int bup_apply_acl_string(const char *name, const char *s)
+{
+ acl_t acl = acl_from_text(s);
+ int ret = 0;
+
+ if (!acl) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return -1;
+ }
+
+ if (acl_set_file(name, ACL_TYPE_ACCESS, acl)) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ ret = -1;
+ }
+
+ acl_free(acl);
+
+ return ret;
+}
+
+static PyObject *bup_apply_acl(PyObject *self, PyObject *args)
+{
+ char *name, *acl, *def = NULL;
+
+ if (!PyArg_ParseTuple(args, cstr_argf cstr_argf "|" cstr_argf, &name, &acl, &def))
+ return NULL;
+
+ if (bup_apply_acl_string(name, acl))
+ return NULL;
+
+ if (def && bup_apply_acl_string(name, def))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+#endif
+
static PyMethodDef helper_methods[] = {
{ "write_sparsely", bup_write_sparsely, METH_VARARGS,
"Write buf excepting zeros at the end. Return trailing zero count." },
" not exist." },
{ "gethostname", bup_gethostname, METH_NOARGS,
"Return the current hostname (as bytes)" },
+#ifdef BUP_HAVE_READLINE
+ { "set_completion_entry_function", bup_set_completion_entry_function, METH_VARARGS,
+ "Set rl_completion_entry_function. Called as f(text, state)." },
+ { "set_attempted_completion_function", bup_set_attempted_completion_function, METH_VARARGS,
+ "Set rl_attempted_completion_function. Called as f(text, start, end)." },
+ { "parse_and_bind", bup_parse_and_bind, METH_VARARGS,
+ "Call rl_parse_and_bind." },
+ { "get_line_buffer", bup_get_line_buffer, METH_NOARGS,
+ "Return rl_line_buffer." },
+ { "get_completer_word_break_characters", bup_get_completer_word_break_characters, METH_NOARGS,
+ "Return rl_completer_word_break_characters." },
+ { "set_completer_word_break_characters", bup_set_completer_word_break_characters, METH_VARARGS,
+ "Set rl_completer_word_break_characters." },
+ { "readline", bup_readline, METH_VARARGS,
+ "Call readline(prompt)." },
+#endif // defined BUP_HAVE_READLINE
+#ifdef ACL_SUPPORT
+ { "read_acl", bup_read_acl, METH_VARARGS,
+ "read_acl(name, isdir)\n\n"
+ "Read ACLs for the given file/dirname and return the correctly encoded"
+ " list [txt, num, def_tx, def_num] (the def_* being empty bytestrings"
+ " unless the second argument 'isdir' is True)." },
+ { "apply_acl", bup_apply_acl, METH_VARARGS,
+ "apply_acl(name, acl, def=None)\n\n"
+ "Given a file/dirname (bytes) and the ACLs to restore, do that." },
+#endif /* HAVE_ACLS */
{ NULL, NULL, 0, NULL }, // sentinel
};