Skip to content

Instantly share code, notes, and snippets.

@nemith
Created January 5, 2013 04:29
Show Gist options
  • Select an option

  • Save nemith/4459752 to your computer and use it in GitHub Desktop.

Select an option

Save nemith/4459752 to your computer and use it in GitHub Desktop.
#include <Python.h>
/* libedit */
#include <histedit.h>
#include <structmember.h>
/* Prompt generating functions added via EL_PROMPT, EL_RPROMPT */
//typedef char *(*pfunc_t)(EditLine *);
/* User functions added via EL_ADDFN */
typedef char (*ufunc_t)(EditLine *e, int ch);
char uf_wrapper(EditLine*, int, int);
#define MAX_USER_FUNC 32
#define UF(x) \
static char uf##x(EditLine *e, int ch) { return uf_wrapper(e, ch, x); }
#define UF_NAME(x) uf##x
/* Define a unqiue function to call for custom user added functions added via
the EL_ADDFN method. This count need to match MAX_USER_FUNC. */
UF(0) UF(1) UF(2) UF(3) UF(4) UF(5) UF(6) UF(7)
UF(8) UF(9) UF(10) UF(11) UF(12) UF(13) UF(14) UF(15)
UF(16) UF(17) UF(18) UF(19) UF(20) UF(21) UF(22) UF(23)
UF(24) UF(25) UF(26) UF(27) UF(28) UF(29) UF(30) UF(31)
static ufunc_t uf_tbl[MAX_USER_FUNC] = {
UF_NAME(0), UF_NAME(1), UF_NAME(2), UF_NAME(3), UF_NAME(4), UF_NAME(5), UF_NAME(6), UF_NAME(7),
UF_NAME(8), UF_NAME(9), UF_NAME(10), UF_NAME(11), UF_NAME(12), UF_NAME(13), UF_NAME(14), UF_NAME(15),
UF_NAME(16), UF_NAME(17), UF_NAME(18), UF_NAME(19), UF_NAME(20), UF_NAME(21), UF_NAME(22), UF_NAME(23),
UF_NAME(24), UF_NAME(25), UF_NAME(26), UF_NAME(27), UF_NAME(28), UF_NAME(29), UF_NAME(30), UF_NAME(31)
};
typedef struct {
PyObject *method;
char *name;
} um_info_t;
typedef struct {
PyObject *prompt_object; /* callable or object as a string */
PyObject *rprompt_object; /* callable or object as a string */
um_info_t *user_methods[MAX_USER_FUNC];
} sess_data_t;
typedef struct {
PyObject_HEAD
EditLine *el; /* EditLine wrapped object */
} editline_EditLine;
char *call_prompt_method(EditLine *el, PyObject **prompt_object) {
PyObject *r = NULL;
char *result = NULL;
if (*prompt_object != NULL) {
if (PyCallable_Check(*prompt_object)) {
r = PyObject_CallObject(*prompt_object, NULL);
if (r == NULL) {
/* HACK: Force an enter to return to python enviroment to raise the
error */
el_push(el, "\n");
return "";
}
result = PyBytes_AsString(PyUnicode_AsASCIIString(r));
}
}
Py_DECREF(r);
return result;
}
char *prompt(EditLine *el) {
sess_data_t *sd;
if (el_get(el, EL_CLIENTDATA, &sd) == 0) {
return call_prompt_method(el, &sd->prompt_object);
}
/* XXX: Raise error here */
return "";
}
char *rprompt(EditLine *el) {
sess_data_t *sd;
if (el_get(el, EL_CLIENTDATA, &sd) == 0) {
return call_prompt_method(el, &sd->rprompt_object);
}
/* XXX: Raise error here */
return "";
}
char uf_wrapper(EditLine *el, int ch, int idx) {
sess_data_t *sd;
PyObject *result = NULL;
if (el_get(el, EL_CLIENTDATA, &sd) == 0) {
um_info_t *um_info = sd->user_methods[idx];
if (um_info && um_info->method) {
if (PyCallable_Check(um_info->method)) {
result = PyObject_CallObject(um_info->method, NULL);
}
if (result == Py_None)
return CC_NORM;
return PyLong_AsLong(result);
}
}
return CC_FATAL;
}
static void EditLine_dealloc(editline_EditLine* self) {
el_end(self->el);
Py_TYPE(self)->tp_free((PyObject*)self);
}
static int EditLine_init(editline_EditLine *self, PyObject *args, PyObject *kwds) {
self->el = el_init("prog", stdin, stdout, stderr);
sess_data_t *sd;
sd = malloc(sizeof(sess_data_t));
sd->prompt_object = NULL;
sd->rprompt_object = NULL;
el_set(self->el, EL_CLIENTDATA, sd);
return 1;
}
static PyMemberDef EditLine_members[] = {
// {"prompt", T_OBJECT_EX, offsetof(editline_EditLine, prompt), 0
// "prompt method or string"},
{NULL} /* Sentinel */
};
static PyObject *
set_prompt_method(PyObject **prompt_object, PyObject *args) {
PyObject *temp;
if (!PyArg_ParseTuple(args, "O", &temp))
return NULL;
/* Since we'll call this from C later lets try to run it once and
see if there are errors now. Not 100% but may get some of the
easy errors */
if (PyObject_CallObject(temp, NULL) == NULL)
return NULL;
Py_XINCREF(temp);
Py_XDECREF(*prompt_object);
*prompt_object = temp;
Py_RETURN_NONE;
}
static PyObject *
EditLine_set_prompt(editline_EditLine *self, PyObject *args, PyObject *kwds) {
sess_data_t *sd;
el_get(self->el, EL_CLIENTDATA, &sd);
el_set(self->el, EL_PROMPT, &prompt);
return set_prompt_method(&sd->prompt_object, args);
}
static PyObject *
EditLine_set_rprompt(editline_EditLine *self, PyObject *args, PyObject *kwds) {
sess_data_t *sd;
el_get(self->el, EL_CLIENTDATA, &sd);
el_set(self->el, EL_RPROMPT, &rprompt);
return set_prompt_method(&sd->rprompt_object, args);
}
static int
EditLine_refresh(editline_EditLine *self, PyObject *args) {
int ret;
ret = el_set(self->el, EL_REFRESH);
return ret;
}
static PyObject *
EditLine_set_editor(editline_EditLine *self, PyObject *args, PyObject *kwds) {
char *editor_mode;
if (!PyArg_ParseTuple(args, "s", &editor_mode))
return NULL;
el_set(self->el, EL_EDITOR, editor_mode);
Py_RETURN_NONE;
}
static PyObject *
EditLine_set_terminal(editline_EditLine *self, PyObject *args, PyObject *kwds) {
char *terminal_mode;
if (!PyArg_ParseTuple(args, "s", &terminal_mode))
return NULL;
el_set(self->el, EL_TERMINAL, *terminal_mode);
Py_RETURN_NONE;
}
/* EL_SIGNAL */
/* EL_BIND */
static PyObject *
EditLine_bindkey(editline_EditLine *self, PyObject *args, PyObject *kwds) {
char *bound_key, *func_name = NULL;
if (!PyArg_ParseTuple(args, "ss", &bound_key, &func_name))
return NULL;
/* if (PyCallable_Check(method)) {
if (!method_name)
sprintf(method_name, "%s_%s", PyEval_GetFuncName(method), PyLong_FromVoidPtr(method));
for (int i = 0, i < MAX_USER_FUNC; i++) {
}
el_set(self->el, EL_ADDFN, func_name, PyEval_GetFuncDesc(cmd), bound);
} */
el_set(self->el, EL_BIND, bound_key, func_name, NULL);
Py_RETURN_NONE;
}
static PyObject *
EditLine_addfn(editline_EditLine *self, PyObject *args, PyObject *kwds) {
char *func_name = NULL;
char *func_help = "";
PyObject *method;
if (!PyArg_ParseTuple(args, "sO|s", &func_name, &method, &func_help))
return NULL;
sess_data_t *sd;
if (el_get(self->el, EL_CLIENTDATA, &sd) != 0)
return NULL;
if (PyCallable_Check(method)) {
for (int volatile i = 0; i < MAX_USER_FUNC; i++) {
if (sd->user_methods[i] == NULL) {
if ((sd->user_methods[i] = malloc(sizeof(um_info_t))) == 0)
return PyErr_NoMemory();
sd->user_methods[i]->method = method;
sd->user_methods[i]->name = func_name;
el_set(self->el, EL_ADDFN, func_name, func_help, uf_tbl[i]);
break;
}
}
}
Py_RETURN_NONE;
}
static PyObject *
EditLine_gets(editline_EditLine *self, PyObject *args, PyObject *kwds) {
int count;
const char *line;
line = el_gets(self->el, &count);
if (count > 0 )
return PyUnicode_FromFormat("%s", line);
Py_RETURN_NONE;
}
static PyMethodDef EditLine_methods[] = {
{"gets", (PyCFunction)EditLine_gets, METH_NOARGS,
"Read a line from the tty."
},
{"set_prompt", (PyCFunction)EditLine_set_prompt, METH_VARARGS,
"Set the prompt to a string or function that return a string."
},
{"refresh", (PyCFunction)EditLine_refresh, METH_NOARGS,
"Re-display the current line on the next terminal line."
},
{"set_rprompt", (PyCFunction)EditLine_set_rprompt, METH_VARARGS,
"Set the right prompt to a string or function that return a string."
},
{"set_editor", (PyCFunction)EditLine_set_editor, METH_VARARGS,
"Set editing mode to mode, which must be one of 'emacs' or 'vi'."
},
{"set_terminal", (PyCFunction)EditLine_set_terminal, METH_VARARGS,
"Define terminal type of the tty to be type, or to TERM if type is None."
},
{"bindkey", (PyCFunction)EditLine_bindkey, METH_VARARGS,
"Bind a key to a function."
},
{"addfn", (PyCFunction)EditLine_addfn, METH_VARARGS,
"Add a user defined function."
},
{NULL} /* Sentinel */
};
static PyTypeObject editline_EditLineType = {
PyVarObject_HEAD_INIT(NULL, 0)
"editline.EditLine", /* tp_name */
sizeof(editline_EditLine), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)EditLine_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"EditLine objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
EditLine_methods, /* tp_methods */
EditLine_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)EditLine_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
static PyModuleDef editlinemodule = {
PyModuleDef_HEAD_INIT,
"editline",
"Example module that creates an extension type.",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC
PyInit_editline(void)
{
PyObject* m;
editline_EditLineType.tp_new = PyType_GenericNew;
if (PyType_Ready(&editline_EditLineType) < 0)
return NULL;
m = PyModule_Create(&editlinemodule);
if (m == NULL)
return NULL;
Py_INCREF(&editline_EditLineType);
PyModule_AddObject(m, "EditLine", (PyObject *)&editline_EditLineType);
// Custom function return values
PyModule_AddIntConstant(m, "CC_NORM", CC_NORM);
PyModule_AddIntConstant(m, "CC_NEWLINE", CC_NEWLINE);
PyModule_AddIntConstant(m, "CC_EOF", CC_EOF);
PyModule_AddIntConstant(m, "CC_ARGHACK", CC_ARGHACK);
PyModule_AddIntConstant(m, "CC_REFRESH", CC_REFRESH);
PyModule_AddIntConstant(m, "CC_REFRESH_BEEP", CC_REFRESH_BEEP);
PyModule_AddIntConstant(m, "CC_CURSOR", CC_CURSOR);
PyModule_AddIntConstant(m, "CC_REDISPLAY", CC_REDISPLAY);
PyModule_AddIntConstant(m, "CC_ERROR", CC_ERROR);
PyModule_AddIntConstant(m, "CC_FATAL", CC_FATAL);
return m;
}
homestar:editline bbennett$ python3 test.py
[email protected]>
% You typed ''
[email protected]>
% You typed ''
[email protected]> Hi mom
% You typed 'Hi mom'
[email protected]>
You hit the question marks!
quit Quit this shit yo
[email protected]>
import editline
import os
import pwd
import socket
def get_username():
return pwd.getpwuid(os.getuid())[0]
def prompt():
return "%s@%s> " % (get_username(), socket.gethostname())
def help():
print("\nYou hit the question marks!")
print("\tquit\tQuit this shit yo")
return editline.CC_REDISPLAY
el = editline.EditLine()
el.set_prompt(prompt)
el.addfn('help', help)
el.bindkey('?', 'help')
while (1):
line = el.gets();
print("%% You typed '%s'" % line.strip())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment