| 1 | #include <errno.h> |
|---|
| 2 | #include "Python.h" |
|---|
| 3 | |
|---|
| 4 | /* |
|---|
| 5 | * Known bugs: |
|---|
| 6 | * - Setting first_prefix to [None] causes a segfault. |
|---|
| 7 | * - Passing encoding to __init__ causes a segfault. |
|---|
| 8 | * - Passing Unicode objects that cannot be |
|---|
| 9 | * decoded by the encoding to write causes an |
|---|
| 10 | * EncodingError to be raised, even though we |
|---|
| 11 | * use "replace". :-\ |
|---|
| 12 | */ |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | /* Duplicating this is annoying, but we need to |
|---|
| 17 | access it from the C level, so we do. */ |
|---|
| 18 | static PyObject *StreamClosed; |
|---|
| 19 | |
|---|
| 20 | /* PlainTextFormatter is abbreviated to PTF */ |
|---|
| 21 | |
|---|
| 22 | typedef struct { |
|---|
| 23 | PyObject_HEAD |
|---|
| 24 | |
|---|
| 25 | /* Public attrs */ |
|---|
| 26 | PyObject *stream; /* This is actually the write method of stream if it's not a file */ |
|---|
| 27 | PyObject *first_prefix; |
|---|
| 28 | PyObject *later_prefix; |
|---|
| 29 | |
|---|
| 30 | /* need these for TermInfoFormatter */ |
|---|
| 31 | PyObject *reset; |
|---|
| 32 | PyObject *bold; |
|---|
| 33 | PyObject *underline; |
|---|
| 34 | char *encoding; |
|---|
| 35 | int autoline; |
|---|
| 36 | int wrap; |
|---|
| 37 | long width; |
|---|
| 38 | |
|---|
| 39 | /* Private */ |
|---|
| 40 | PyObject *stored_stream; |
|---|
| 41 | PyObject *stored_autoline; |
|---|
| 42 | int pos; |
|---|
| 43 | int in_first_line; |
|---|
| 44 | int wrote_something; |
|---|
| 45 | |
|---|
| 46 | int stream_is_file; |
|---|
| 47 | } PTF_object; |
|---|
| 48 | |
|---|
| 49 | static PyObject *convert_list(PyObject *s) { |
|---|
| 50 | /* Convenience function, returns *s as list if it isn't one already */ |
|---|
| 51 | if (PyList_Check(s)) |
|---|
| 52 | return s; |
|---|
| 53 | |
|---|
| 54 | /* If it's NULL we return it as we would return NULL anyway... */ |
|---|
| 55 | return PySequence_List(s); |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | /* |
|---|
| 59 | #define IMMUTABLE_ATTR(name) \ |
|---|
| 60 | static int \ |
|---|
| 61 | PTF_set_##name (PTF_object *self, PyObject *v, void *closure) \ |
|---|
| 62 | { \ |
|---|
| 63 | PyErr_SetString(PyExc_AttributeError, #name" is immutable"); \ |
|---|
| 64 | return -1; \ |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | #define EMPTY_IMMUTABLE_CONST(name) \ |
|---|
| 68 | {#name, \ |
|---|
| 69 | (getter)PTF_getemptystring, \ |
|---|
| 70 | (setter)PTF_set_##name, \ |
|---|
| 71 | #name, \ |
|---|
| 72 | NULL} |
|---|
| 73 | |
|---|
| 74 | IMMUTABLE_ATTR(bold) |
|---|
| 75 | IMMUTABLE_ATTR(reset) |
|---|
| 76 | IMMUTABLE_ATTR(underline) |
|---|
| 77 | */ |
|---|
| 78 | |
|---|
| 79 | #define annoying_pyobj_func(name, attr) pyobj_get_func(name, attr) \ |
|---|
| 80 | pyobj_set_func(name, attr) |
|---|
| 81 | |
|---|
| 82 | #define pyobj_get_func(name, attr) static PyObject * \ |
|---|
| 83 | PTF_getobj_##name(PTF_object *self, void *closure) \ |
|---|
| 84 | { \ |
|---|
| 85 | Py_INCREF(self->attr); \ |
|---|
| 86 | return self->attr; \ |
|---|
| 87 | } \ |
|---|
| 88 | |
|---|
| 89 | #define pyobj_set_func(name, attr) static int \ |
|---|
| 90 | PTF_setobj_##name(PTF_object *self, PyObject *value, void *closure)\ |
|---|
| 91 | { \ |
|---|
| 92 | if (value == NULL) { \ |
|---|
| 93 | PyErr_SetString(PyExc_TypeError, \ |
|---|
| 94 | "Cannot delete the "#name" attribute"); \ |
|---|
| 95 | return -1; \ |
|---|
| 96 | } \ |
|---|
| 97 | Py_DECREF(self->attr); \ |
|---|
| 98 | Py_INCREF(value); \ |
|---|
| 99 | self->attr = value; \ |
|---|
| 100 | return 0; \ |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | #define pyobj_func(name) annoying_pyobj_func(name, name) |
|---|
| 104 | |
|---|
| 105 | pyobj_func(first_prefix) |
|---|
| 106 | pyobj_func(later_prefix) |
|---|
| 107 | pyobj_func(bold) |
|---|
| 108 | pyobj_func(underline) |
|---|
| 109 | pyobj_func(reset) |
|---|
| 110 | |
|---|
| 111 | pyobj_get_func(autoline, stored_autoline) |
|---|
| 112 | |
|---|
| 113 | static PyObject * |
|---|
| 114 | PTF_getwidth(PTF_object *self, void *closure) |
|---|
| 115 | { |
|---|
| 116 | return PyInt_FromLong(self->width); |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | static int |
|---|
| 120 | PTF_setwidth(PTF_object *self, PyObject *value, void *closure) |
|---|
| 121 | { |
|---|
| 122 | long tmp = PyInt_AsLong(value); |
|---|
| 123 | if (tmp == -1 && PyErr_Occurred()) |
|---|
| 124 | return -1; |
|---|
| 125 | self->width = tmp; |
|---|
| 126 | return 0; |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | static int |
|---|
| 130 | PTF_setautoline(PTF_object *self, PyObject *value, void *closure) |
|---|
| 131 | { |
|---|
| 132 | int tmp; |
|---|
| 133 | |
|---|
| 134 | if (!value) { |
|---|
| 135 | PyErr_SetString(PyExc_TypeError, "Cannot delete the autoline attribute"); |
|---|
| 136 | return -1; |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | tmp = PyObject_IsTrue(value); |
|---|
| 140 | if (tmp == -1) |
|---|
| 141 | return -1; |
|---|
| 142 | |
|---|
| 143 | self->autoline = tmp; |
|---|
| 144 | self->stored_autoline = value; |
|---|
| 145 | return 0; |
|---|
| 146 | } |
|---|
| 147 | |
|---|
| 148 | static int |
|---|
| 149 | PTF_setprefix(PTF_object *self, PyObject *value, char closure) |
|---|
| 150 | { |
|---|
| 151 | PyObject *tmp = convert_list(value); |
|---|
| 152 | |
|---|
| 153 | if (!tmp) |
|---|
| 154 | return -1; |
|---|
| 155 | |
|---|
| 156 | if (closure == 'f') |
|---|
| 157 | self->first_prefix = tmp; |
|---|
| 158 | else if (closure == 'l') |
|---|
| 159 | self->later_prefix = tmp; |
|---|
| 160 | |
|---|
| 161 | return 0; |
|---|
| 162 | } |
|---|
| 163 | |
|---|
| 164 | static PyObject * |
|---|
| 165 | PTF_returnemptystring(PTF_object *self, PyObject *args) |
|---|
| 166 | { |
|---|
| 167 | char *s; |
|---|
| 168 | if (!PyArg_ParseTuple(args, "|s", s)) |
|---|
| 169 | return NULL; |
|---|
| 170 | |
|---|
| 171 | PyObject *ret = PyString_FromString(""); |
|---|
| 172 | if (!ret) |
|---|
| 173 | return NULL; |
|---|
| 174 | return ret; |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | |
|---|
| 178 | static PyObject * |
|---|
| 179 | PTF_getstream(PTF_object *self, void *closure) |
|---|
| 180 | { |
|---|
| 181 | Py_INCREF(self->stored_stream); |
|---|
| 182 | return self->stored_stream; |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | |
|---|
| 186 | static int |
|---|
| 187 | PTF_setstream(PTF_object *self, PyObject *value, void *closure) |
|---|
| 188 | { |
|---|
| 189 | PyObject *tmp; |
|---|
| 190 | if (value == NULL) { |
|---|
| 191 | PyErr_SetString(PyExc_TypeError, "Cannot delete the stream attribute"); |
|---|
| 192 | return -1; |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | if (PyFile_Check(value)) { |
|---|
| 196 | self->stream_is_file = 1; |
|---|
| 197 | self->stream = value; |
|---|
| 198 | } else { |
|---|
| 199 | tmp = PyObject_GetAttrString(value, "write"); |
|---|
| 200 | if (!tmp) { |
|---|
| 201 | if (!PyErr_Occurred()) |
|---|
| 202 | PyErr_SetString(PyExc_TypeError, "stream has no write method"); |
|---|
| 203 | return -1; |
|---|
| 204 | } |
|---|
| 205 | self->stream_is_file = 0; |
|---|
| 206 | self->stream = tmp; |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | Py_XDECREF(self->stored_stream); |
|---|
| 210 | Py_INCREF(value); |
|---|
| 211 | self->stored_stream = value; |
|---|
| 212 | |
|---|
| 213 | return 0; |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | |
|---|
| 217 | static int |
|---|
| 218 | PTF_traverse(PTF_object *self, visitproc visit, void *arg) |
|---|
| 219 | { |
|---|
| 220 | Py_VISIT(self->stored_stream); |
|---|
| 221 | Py_VISIT(self->first_prefix); |
|---|
| 222 | Py_VISIT(self->later_prefix); |
|---|
| 223 | Py_VISIT(self->reset); |
|---|
| 224 | Py_VISIT(self->bold); |
|---|
| 225 | Py_VISIT(self->underline); |
|---|
| 226 | return 0; |
|---|
| 227 | } |
|---|
| 228 | |
|---|
| 229 | static int |
|---|
| 230 | PTF_clear(PTF_object *self) |
|---|
| 231 | { |
|---|
| 232 | Py_CLEAR(self->stored_stream); |
|---|
| 233 | Py_CLEAR(self->first_prefix); |
|---|
| 234 | Py_CLEAR(self->later_prefix); |
|---|
| 235 | Py_CLEAR(self->reset); |
|---|
| 236 | Py_CLEAR(self->bold); |
|---|
| 237 | Py_CLEAR(self->underline); |
|---|
| 238 | return 0; |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | static void |
|---|
| 242 | PTF_dealloc(PTF_object *self) { |
|---|
| 243 | PTF_clear(self); |
|---|
| 244 | self->ob_type->tp_free((PyObject*)self); |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | #if 0 |
|---|
| 248 | /* Public attrs */ |
|---|
| 249 | PyObject *stream; /* This is actually the write method of stream if it's not a file */ |
|---|
| 250 | PyObject *first_prefix; |
|---|
| 251 | PyObject *later_prefix; |
|---|
| 252 | |
|---|
| 253 | /* need these for TermInfoFormatter */ |
|---|
| 254 | PyObject *reset; |
|---|
| 255 | PyObject *bold; |
|---|
| 256 | PyObject *underline; |
|---|
| 257 | char *encoding; |
|---|
| 258 | int autoline; |
|---|
| 259 | int wrap; |
|---|
| 260 | int width; |
|---|
| 261 | |
|---|
| 262 | /* Private */ |
|---|
| 263 | PyObject *stored_stream; |
|---|
| 264 | int pos; |
|---|
| 265 | int in_first_line; |
|---|
| 266 | int wrote_something; |
|---|
| 267 | |
|---|
| 268 | int stream_is_file; |
|---|
| 269 | #endif |
|---|
| 270 | |
|---|
| 271 | #define blank_string(what) self->what = PyString_FromString(""); \ |
|---|
| 272 | if (!self->what) \ |
|---|
| 273 | goto error |
|---|
| 274 | |
|---|
| 275 | static PyObject * |
|---|
| 276 | PTF_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
|---|
| 277 | { |
|---|
| 278 | PTF_object *self; |
|---|
| 279 | self = (PTF_object *)type->tp_alloc(type, 0); |
|---|
| 280 | |
|---|
| 281 | /* Private */ |
|---|
| 282 | self->autoline = 1; |
|---|
| 283 | self->wrap = 0; |
|---|
| 284 | self->pos = 0; |
|---|
| 285 | self->in_first_line = 1; |
|---|
| 286 | self->wrote_something = 0; |
|---|
| 287 | |
|---|
| 288 | /* Defaults */ |
|---|
| 289 | self->width = 79; |
|---|
| 290 | self->encoding = "ascii"; /* this should pick up on the system default but i'm lazy. |
|---|
| 291 | * So sue me. */ |
|---|
| 292 | self->first_prefix = PyList_New(0); |
|---|
| 293 | self->later_prefix = PyList_New(0); |
|---|
| 294 | |
|---|
| 295 | Py_INCREF(Py_True); |
|---|
| 296 | self->stored_autoline = Py_True; |
|---|
| 297 | |
|---|
| 298 | blank_string(bold); |
|---|
| 299 | blank_string(reset); |
|---|
| 300 | blank_string(underline); |
|---|
| 301 | return (PyObject *)self; |
|---|
| 302 | error: |
|---|
| 303 | return NULL; |
|---|
| 304 | } |
|---|
| 305 | |
|---|
| 306 | static int |
|---|
| 307 | PTF_init(PTF_object *self, PyObject *args, PyObject *kwds) |
|---|
| 308 | { |
|---|
| 309 | PyObject *encoding=NULL; |
|---|
| 310 | char *s; |
|---|
| 311 | static char *kwlist[] = {"stream", "width", "encoding", NULL}; |
|---|
| 312 | |
|---|
| 313 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iz", kwlist, |
|---|
| 314 | &self->stream, &self->width, &encoding)) |
|---|
| 315 | return -1; |
|---|
| 316 | |
|---|
| 317 | if (encoding != NULL && encoding != Py_None) { |
|---|
| 318 | s = PyString_AsString(encoding); |
|---|
| 319 | if (!s) |
|---|
| 320 | return -1; |
|---|
| 321 | printf("setting encoding to %s\n", s); |
|---|
| 322 | self->encoding = s; |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | /* Seems kinda stupid to do this, but it needs to be done. */ |
|---|
| 326 | PTF_setstream(self, self->stream, NULL); |
|---|
| 327 | |
|---|
| 328 | return 0; |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | |
|---|
| 332 | static int |
|---|
| 333 | _write_prefix(PTF_object *self, int wrap) { |
|---|
| 334 | PyObject *prefix, *iter, *arg=NULL, *tmp, *stream; |
|---|
| 335 | |
|---|
| 336 | if (self->in_first_line) |
|---|
| 337 | prefix = self->first_prefix; |
|---|
| 338 | else |
|---|
| 339 | prefix = self->later_prefix; |
|---|
| 340 | |
|---|
| 341 | iter = PyObject_GetIter(prefix); |
|---|
| 342 | if (!iter) |
|---|
| 343 | return -1; |
|---|
| 344 | while ((arg = PyIter_Next(iter))) { |
|---|
| 345 | while ((PyCallable_Check(arg))) { |
|---|
| 346 | tmp = PyObject_CallFunctionObjArgs(arg, (PyObject*)self, NULL); |
|---|
| 347 | if (!tmp) |
|---|
| 348 | goto error; |
|---|
| 349 | Py_DECREF(arg); |
|---|
| 350 | arg = tmp; |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | if (arg == Py_None) |
|---|
| 354 | continue; |
|---|
| 355 | |
|---|
| 356 | if (PyUnicode_Check(arg)) { |
|---|
| 357 | tmp = PyUnicode_AsEncodedString(arg, self->encoding, "replace"); |
|---|
| 358 | if (!tmp) |
|---|
| 359 | goto error; |
|---|
| 360 | Py_DECREF(arg); |
|---|
| 361 | arg = tmp; |
|---|
| 362 | } |
|---|
| 363 | |
|---|
| 364 | if (!(PyString_Check(arg))) { |
|---|
| 365 | tmp = PyObject_Str(arg); |
|---|
| 366 | if (!tmp) |
|---|
| 367 | goto error; |
|---|
| 368 | Py_DECREF(arg); |
|---|
| 369 | arg = tmp; |
|---|
| 370 | } |
|---|
| 371 | |
|---|
| 372 | if (self->stream_is_file) { |
|---|
| 373 | if (PyFile_WriteObject(arg, stream, Py_PRINT_RAW)) |
|---|
| 374 | goto error; |
|---|
| 375 | } else { |
|---|
| 376 | if (!PyObject_CallFunctionObjArgs(self->stream, arg, NULL)) |
|---|
| 377 | goto error; |
|---|
| 378 | } |
|---|
| 379 | |
|---|
| 380 | if (wrap && (self->pos >= self->width)) |
|---|
| 381 | self->pos = self->width-10; |
|---|
| 382 | } |
|---|
| 383 | return 0; |
|---|
| 384 | error: |
|---|
| 385 | return -1; |
|---|
| 386 | } |
|---|
| 387 | |
|---|
| 388 | #define getitem(arg) arg = PyDict_GetItemString(kwargs, #arg); \ |
|---|
| 389 | if (!arg && PyErr_Occurred()) \ |
|---|
| 390 | goto error |
|---|
| 391 | |
|---|
| 392 | static PyObject * |
|---|
| 393 | PTF_write(PTF_object *self, PyObject *args, PyObject *kwargs) { |
|---|
| 394 | PyObject *wrap=NULL, *autoline=NULL, *prefixes=NULL, *prefix=NULL; |
|---|
| 395 | PyObject *first_prefixes=NULL, *later_prefixes=NULL; |
|---|
| 396 | PyObject *first_prefix=NULL, *later_prefix=NULL; |
|---|
| 397 | PyObject *tmp=NULL, *arg=NULL; |
|---|
| 398 | PyObject *iterator=NULL, *bit=NULL, *e=NULL; |
|---|
| 399 | |
|---|
| 400 | int i, maxlen, space, i_autoline; |
|---|
| 401 | char *p; |
|---|
| 402 | |
|---|
| 403 | wrap = self->wrap; |
|---|
| 404 | if ((kwargs)) { |
|---|
| 405 | getitem(prefixes); |
|---|
| 406 | getitem(prefix); |
|---|
| 407 | getitem(first_prefixes); |
|---|
| 408 | getitem(later_prefixes); |
|---|
| 409 | getitem(later_prefix); |
|---|
| 410 | getitem(wrap); |
|---|
| 411 | getitem(autoline); |
|---|
| 412 | } |
|---|
| 413 | |
|---|
| 414 | |
|---|
| 415 | |
|---|
| 416 | /* "args", "prefixes", "prefix", "first_prefixes", "first_prefix", "later_prefixes", "later_prefix", "wrap", "autoline", NULL}; |
|---|
| 417 | |
|---|
| 418 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOOOOOO", kwlist, |
|---|
| 419 | &args_t, &prefixes, &prefix, &first_prefixes, &first_prefix, &later_prefixes, &later_prefix, &wrap, &autoline)) { |
|---|
| 420 | return NULL; |
|---|
| 421 | }*/ |
|---|
| 422 | |
|---|
| 423 | |
|---|
| 424 | if (autoline) { |
|---|
| 425 | i_autoline = PyObject_IsTrue(autoline); |
|---|
| 426 | if (i_autoline == -1) |
|---|
| 427 | goto error; |
|---|
| 428 | } else { |
|---|
| 429 | i_autoline = self->autoline; |
|---|
| 430 | } |
|---|
| 431 | |
|---|
| 432 | if ((prefixes)) { |
|---|
| 433 | if ((first_prefixes) || (later_prefixes)) { |
|---|
| 434 | PyErr_SetString(PyExc_TypeError, |
|---|
| 435 | "do not pass first_prefixes or later_prefixes " |
|---|
| 436 | "if prefixes is passed"); |
|---|
| 437 | goto error; |
|---|
| 438 | } |
|---|
| 439 | first_prefixes = later_prefixes = prefixes; |
|---|
| 440 | } |
|---|
| 441 | |
|---|
| 442 | |
|---|
| 443 | if ((prefix)) { |
|---|
| 444 | if ((first_prefix) || (later_prefix)) { |
|---|
| 445 | PyErr_SetString(PyExc_TypeError, |
|---|
| 446 | "do not pass first_prefix or later_prefix with prefix"); |
|---|
| 447 | goto error; |
|---|
| 448 | } |
|---|
| 449 | first_prefix = later_prefix = prefix; |
|---|
| 450 | } |
|---|
| 451 | |
|---|
| 452 | if ((first_prefix)) { |
|---|
| 453 | if ((first_prefixes)) { |
|---|
| 454 | PyErr_SetString(PyExc_TypeError, |
|---|
| 455 | "do not pass both first_prefix and first_prefixes"); |
|---|
| 456 | goto error; |
|---|
| 457 | } |
|---|
| 458 | first_prefixes = PyTuple_Pack(1, first_prefix); |
|---|
| 459 | } |
|---|
| 460 | |
|---|
| 461 | if ((later_prefix)) { |
|---|
| 462 | if (later_prefixes) { |
|---|
| 463 | PyErr_SetString(PyExc_TypeError, |
|---|
| 464 | "do not pass both later_prefix and later_prefixes"); |
|---|
| 465 | goto error; |
|---|
| 466 | } |
|---|
| 467 | later_prefixes = PyTuple_Pack(1, later_prefix); |
|---|
| 468 | } |
|---|
| 469 | |
|---|
| 470 | if ((first_prefixes)) |
|---|
| 471 | if (!_PyList_Extend(self->first_prefix, first_prefixes)) |
|---|
| 472 | goto error; |
|---|
| 473 | |
|---|
| 474 | if ((later_prefixes)) |
|---|
| 475 | if (!_PyList_Extend(self->later_prefix, later_prefixes)) |
|---|
| 476 | goto error; |
|---|
| 477 | |
|---|
| 478 | iterator = PyObject_GetIter(args); |
|---|
| 479 | while (arg = PyIter_Next(iterator)) { |
|---|
| 480 | /* If we're at the start of the line, write our prefix. |
|---|
| 481 | * There is a deficiency here: if neither our arg nor our |
|---|
| 482 | * prefix affect _pos (both are escape sequences or empty) |
|---|
| 483 | * we will write prefix more than once. This should not |
|---|
| 484 | * matter. |
|---|
| 485 | */ |
|---|
| 486 | if (self->pos == 0) |
|---|
| 487 | if (_write_prefix(self, wrap)) |
|---|
| 488 | goto finally; |
|---|
| 489 | |
|---|
| 490 | |
|---|
| 491 | while (PyCallable_Check(arg)) { |
|---|
| 492 | arg = PyObject_CallFunctionObjArgs(arg, (PyObject*)self, NULL); |
|---|
| 493 | if (!arg) |
|---|
| 494 | goto finally; |
|---|
| 495 | } |
|---|
| 496 | |
|---|
| 497 | if (arg == Py_None) |
|---|
| 498 | continue; |
|---|
| 499 | |
|---|
| 500 | if (!PyString_Check(arg)) { |
|---|
| 501 | tmp = PyObject_Str(arg); |
|---|
| 502 | if (!tmp) |
|---|
| 503 | goto finally; |
|---|
| 504 | Py_DECREF(arg); |
|---|
| 505 | arg = tmp; |
|---|
| 506 | } |
|---|
| 507 | |
|---|
| 508 | if (PyUnicode_Check(arg)) { |
|---|
| 509 | tmp = PyUnicode_AsEncodedString(arg, self->encoding, "replace"); |
|---|
| 510 | if (!tmp) |
|---|
| 511 | goto finally; |
|---|
| 512 | Py_DECREF(arg); |
|---|
| 513 | arg = tmp; |
|---|
| 514 | } |
|---|
| 515 | |
|---|
| 516 | if (!PyString_GET_SIZE(arg)) |
|---|
| 517 | /* There's nothing to write, so skip this bit... */ |
|---|
| 518 | continue; |
|---|
| 519 | |
|---|
| 520 | while (wrap && ((self->pos + PyString_GET_SIZE(arg)) > self->width)) { |
|---|
| 521 | /* We have to split. */ |
|---|
| 522 | maxlen = self->width - self->pos; |
|---|
| 523 | p = PyString_AS_STRING(arg); |
|---|
| 524 | for (space = -1, i = 0; *p++, i++;) { |
|---|
| 525 | if (i == maxlen) |
|---|
| 526 | break; |
|---|
| 527 | if (*p == ' ') { |
|---|
| 528 | space = i; |
|---|
| 529 | break; |
|---|
| 530 | } |
|---|
| 531 | } |
|---|
| 532 | |
|---|
| 533 | if (space == -1) { |
|---|
| 534 | /* No space to split on. |
|---|
| 535 | |
|---|
| 536 | * If we are on the first line we can simply go to |
|---|
| 537 | * the next (this helps if the "later" prefix is |
|---|
| 538 | * shorter and should not really matter if not). |
|---|
| 539 | |
|---|
| 540 | * If we are on the second line and have already |
|---|
| 541 | * written something we can also go to the next |
|---|
| 542 | * line. |
|---|
| 543 | */ |
|---|
| 544 | |
|---|
| 545 | if (self->in_first_line || self->wrote_something) { |
|---|
| 546 | bit = PyString_FromString(""); |
|---|
| 547 | if (!bit) |
|---|
| 548 | goto finally; |
|---|
| 549 | } |
|---|
| 550 | else { |
|---|
| 551 | /* Forcibly split this as far to the right as |
|---|
| 552 | * possible. |
|---|
| 553 | */ |
|---|
| 554 | bit = PySequence_GetSlice(arg, 0, maxlen); |
|---|
| 555 | tmp = PySequence_GetSlice(arg, maxlen, 0); |
|---|
| 556 | if (!bit || !tmp) |
|---|
| 557 | goto finally; |
|---|
| 558 | Py_DECREF(arg); |
|---|
| 559 | arg = tmp; |
|---|
| 560 | } |
|---|
| 561 | } else { |
|---|
| 562 | /* Omit the space we split on.*/ |
|---|
| 563 | bit = PySequence_GetSlice(arg, NULL, space); |
|---|
| 564 | tmp = PySequence_GetSlice(arg, space+1, NULL); |
|---|
| 565 | if (!bit || !tmp) |
|---|
| 566 | goto finally; |
|---|
| 567 | Py_DECREF(arg); |
|---|
| 568 | arg = tmp; |
|---|
| 569 | } |
|---|
| 570 | |
|---|
| 571 | if (self->stream_is_file) { |
|---|
| 572 | if (PyFile_WriteObject(bit, self->stream, Py_PRINT_RAW)) |
|---|
| 573 | goto finally; |
|---|
| 574 | } else { |
|---|
| 575 | if (!PyObject_CallFunctionObjArgs(self->stream, bit)) |
|---|
| 576 | goto finally; |
|---|
| 577 | } |
|---|
| 578 | |
|---|
| 579 | self->pos = 0; |
|---|
| 580 | self->in_first_line = 0; |
|---|
| 581 | self->wrote_something = 0; |
|---|
| 582 | if (_write_prefix(self, wrap)) |
|---|
| 583 | goto finally; |
|---|
| 584 | |
|---|
| 585 | } |
|---|
| 586 | |
|---|
| 587 | if (self->stream_is_file) { |
|---|
| 588 | if (PyFile_WriteObject(arg, self->stream, Py_PRINT_RAW)) |
|---|
| 589 | goto finally; |
|---|
| 590 | } else { |
|---|
| 591 | if (!PyObject_CallFunctionObjArgs(self->stream, arg, NULL)) |
|---|
| 592 | goto finally; |
|---|
| 593 | } |
|---|
| 594 | if (!i_autoline) { |
|---|
| 595 | self->wrote_something = 1; |
|---|
| 596 | self->pos += PyString_GET_SIZE(arg); |
|---|
| 597 | } |
|---|
| 598 | } |
|---|
| 599 | |
|---|
| 600 | if (i_autoline) { |
|---|
| 601 | if (self->stream_is_file) { |
|---|
| 602 | if (PyFile_WriteString("\n", self->stream)) |
|---|
| 603 | goto finally; |
|---|
| 604 | } else { |
|---|
| 605 | if (!PyObject_CallFunction(self->stream, "(s)", "\n")) |
|---|
| 606 | goto finally; |
|---|
| 607 | } |
|---|
| 608 | self->in_first_line = 1; |
|---|
| 609 | self->wrote_something = 0; |
|---|
| 610 | self->pos = 0; |
|---|
| 611 | } |
|---|
| 612 | |
|---|
| 613 | finally: |
|---|
| 614 | |
|---|
| 615 | if (first_prefixes) |
|---|
| 616 | PyList_SetSlice(self->first_prefix, -PyList_GET_SIZE(first_prefixes), NULL, NULL); |
|---|
| 617 | if (later_prefixes) |
|---|
| 618 | PyList_SetSlice(self->later_prefix, -PyList_GET_SIZE(later_prefixes), NULL, NULL); |
|---|
| 619 | |
|---|
| 620 | e = PyErr_Occurred(); |
|---|
| 621 | if (e) { |
|---|
| 622 | if (PyErr_ExceptionMatches(PyExc_IOError) && |
|---|
| 623 | PyInt_AS_LONG(PyObject_GetAttrString(e, "errno")) == EPIPE) |
|---|
| 624 | PyErr_SetObject(e, StreamClosed); |
|---|
| 625 | goto error; |
|---|
| 626 | } |
|---|
| 627 | |
|---|
| 628 | Py_RETURN_NONE; |
|---|
| 629 | |
|---|
| 630 | error: |
|---|
| 631 | Py_XDECREF(wrap); |
|---|
| 632 | Py_XDECREF(autoline); |
|---|
| 633 | Py_XDECREF(prefixes); |
|---|
| 634 | Py_XDECREF(first_prefixes); |
|---|
| 635 | Py_XDECREF(later_prefixes); |
|---|
| 636 | Py_XDECREF(tmp); |
|---|
| 637 | return NULL; |
|---|
| 638 | } |
|---|
| 639 | |
|---|
| 640 | static PyMethodDef PTF_methods[] = { |
|---|
| 641 | {"write", (PyCFunction)PTF_write, METH_VARARGS | METH_KEYWORDS, |
|---|
| 642 | "Return the name, combining the first and last name" |
|---|
| 643 | }, |
|---|
| 644 | {"fg", (PyCFunction)PTF_returnemptystring, METH_VARARGS, |
|---|
| 645 | "" |
|---|
| 646 | }, |
|---|
| 647 | {"bg", (PyCFunction)PTF_returnemptystring, METH_VARARGS, |
|---|
| 648 | "" |
|---|
| 649 | }, |
|---|
| 650 | {"title", (PyCFunction)PTF_returnemptystring, METH_VARARGS, |
|---|
| 651 | "" |
|---|
| 652 | }, |
|---|
| 653 | {NULL} /* Sentinel */ |
|---|
| 654 | }; |
|---|
| 655 | |
|---|
| 656 | #define pyobj_struct(name) {#name, \ |
|---|
| 657 | (getter)PTF_getobj_##name, (setter)PTF_setobj_##name, \ |
|---|
| 658 | #name, \ |
|---|
| 659 | NULL} |
|---|
| 660 | |
|---|
| 661 | |
|---|
| 662 | static PyGetSetDef PTF_getseters[] = { |
|---|
| 663 | {"stream", |
|---|
| 664 | (getter)PTF_getstream, (setter)PTF_setstream, |
|---|
| 665 | "stream to write to", |
|---|
| 666 | 's'}, |
|---|
| 667 | |
|---|
| 668 | {"first_prefix", |
|---|
| 669 | (getter)PTF_getobj_first_prefix, (setter)PTF_setprefix, |
|---|
| 670 | "the first prefix", |
|---|
| 671 | 'f'}, |
|---|
| 672 | |
|---|
| 673 | {"later_prefix", |
|---|
| 674 | (getter)PTF_getobj_later_prefix, (setter)PTF_setprefix, |
|---|
| 675 | "later prefixes", |
|---|
| 676 | 'l'}, |
|---|
| 677 | |
|---|
| 678 | {"autoline", |
|---|
| 679 | (getter)PTF_getobj_autoline, (setter)PTF_setautoline, |
|---|
| 680 | "autoline", |
|---|
| 681 | NULL}, |
|---|
| 682 | |
|---|
| 683 | {"width", |
|---|
| 684 | (getter)PTF_getwidth, (setter)PTF_setwidth, |
|---|
| 685 | "width", |
|---|
| 686 | NULL}, |
|---|
| 687 | |
|---|
| 688 | pyobj_struct(bold), |
|---|
| 689 | pyobj_struct(underline), |
|---|
| 690 | pyobj_struct(reset), |
|---|
| 691 | |
|---|
| 692 | {NULL} /* Sentinel */ |
|---|
| 693 | }; |
|---|
| 694 | |
|---|
| 695 | PyDoc_STRVAR(PTF_doc, |
|---|
| 696 | "PTF(iter1 [,iter2 [...]]) --> izip object\n\ |
|---|
| 697 | \n\ |
|---|
| 698 | Return a PTF object whose .next() method returns a tuple where\n\ |
|---|
| 699 | the i-th element comes from the i-th iterable argument. The .next()\n\ |
|---|
| 700 | method continues until the shortest iterable in the argument sequence\n\ |
|---|
| 701 | is exhausted and then it raises StopIteration. Works like the zip()\n\ |
|---|
| 702 | function but consumes less memory by returning an iterator instead of\n\ |
|---|
| 703 | a list."); |
|---|
| 704 | |
|---|
| 705 | |
|---|
| 706 | static PyTypeObject PTF_type = { |
|---|
| 707 | PyObject_HEAD_INIT(NULL) |
|---|
| 708 | 0, /* ob_size */ |
|---|
| 709 | "formatters.PlainTextFormatter",/* tp_name */ |
|---|
| 710 | sizeof(PTF_object), /* tp_basicsize */ |
|---|
| 711 | 0, /* tp_itemsize */ |
|---|
| 712 | (destructor)PTF_dealloc, /* tp_dealloc */ |
|---|
| 713 | 0, /* tp_print */ |
|---|
| 714 | 0, /* tp_getattr */ |
|---|
| 715 | 0, /* tp_setattr */ |
|---|
| 716 | 0, /* tp_compare */ |
|---|
| 717 | 0, /* tp_repr */ |
|---|
| 718 | 0, /* tp_as_number */ |
|---|
| 719 | 0, /* tp_as_sequence */ |
|---|
| 720 | 0, /* tp_as_mapping */ |
|---|
| 721 | 0, /* tp_hash */ |
|---|
| 722 | 0, /* tp_call */ |
|---|
| 723 | 0, /* tp_str */ |
|---|
| 724 | 0, /* tp_getattro */ |
|---|
| 725 | 0, /* tp_setattro */ |
|---|
| 726 | 0, /* tp_as_buffer */ |
|---|
| 727 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_CLASS | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ |
|---|
| 728 | PTF_doc, /* tp_doc */ |
|---|
| 729 | (traverseproc)PTF_traverse, /* tp_traverse */ |
|---|
| 730 | (inquiry)PTF_clear, /* tp_clear */ |
|---|
| 731 | 0, /* tp_richcompare */ |
|---|
| 732 | 0, /* tp_weaklistoffset */ |
|---|
| 733 | 0, /* tp_iter */ |
|---|
| 734 | 0, /* tp_iternext */ |
|---|
| 735 | PTF_methods, /* tp_methods */ |
|---|
| 736 | 0, /* tp_members */ |
|---|
| 737 | PTF_getseters, /* tp_getset */ |
|---|
| 738 | 0, /* tp_base */ |
|---|
| 739 | 0, /* tp_dict */ |
|---|
| 740 | 0, /* tp_descr_get */ |
|---|
| 741 | 0, /* tp_descr_set */ |
|---|
| 742 | 0, /* tp_dictoffset */ |
|---|
| 743 | (initproc)PTF_init, /* tp_init */ |
|---|
| 744 | 0, /* tp_alloc */ |
|---|
| 745 | PTF_new, /* tp_new */ |
|---|
| 746 | }; |
|---|
| 747 | |
|---|
| 748 | PyDoc_STRVAR(formatters_module_doc, "my funky module\n"); |
|---|
| 749 | |
|---|
| 750 | PyMODINIT_FUNC |
|---|
| 751 | init_formatters() |
|---|
| 752 | { |
|---|
| 753 | PyObject *m = Py_InitModule3("_formatters", NULL, formatters_module_doc); |
|---|
| 754 | if (!m) |
|---|
| 755 | return; |
|---|
| 756 | |
|---|
| 757 | PyObject *stream_closed = PyErr_NewException("snakeoil._formatters.StreamClosed", PyExc_KeyboardInterrupt, NULL); |
|---|
| 758 | Py_INCREF(stream_closed); |
|---|
| 759 | if (PyModule_AddObject(m, "StreamClosed", stream_closed)) |
|---|
| 760 | return; |
|---|
| 761 | |
|---|
| 762 | if (PyType_Ready(&PTF_type) < 0) |
|---|
| 763 | return; |
|---|
| 764 | Py_INCREF(&PTF_type); |
|---|
| 765 | if (PyModule_AddObject(m, "PlainTextFormatter", (PyObject *)&PTF_type) == -1) |
|---|
| 766 | return; |
|---|
| 767 | } |
|---|