]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/python/Python/traceback.c
/sys/lib/dist/mkfile: test for .git directory
[plan9front.git] / sys / src / cmd / python / Python / traceback.c
1
2 /* Traceback implementation */
3
4 #include "Python.h"
5
6 #include "code.h"
7 #include "frameobject.h"
8 #include "structmember.h"
9 #include "osdefs.h"
10 #include "traceback.h"
11
12 #define OFF(x) offsetof(PyTracebackObject, x)
13
14 static struct memberlist tb_memberlist[] = {
15         {"tb_next",     T_OBJECT,       OFF(tb_next)},
16         {"tb_frame",    T_OBJECT,       OFF(tb_frame)},
17         {"tb_lasti",    T_INT,          OFF(tb_lasti)},
18         {"tb_lineno",   T_INT,          OFF(tb_lineno)},
19         {NULL}  /* Sentinel */
20 };
21
22 static PyObject *
23 tb_getattr(PyTracebackObject *tb, char *name)
24 {
25         return PyMember_Get((char *)tb, tb_memberlist, name);
26 }
27
28 static void
29 tb_dealloc(PyTracebackObject *tb)
30 {
31         PyObject_GC_UnTrack(tb);
32         Py_TRASHCAN_SAFE_BEGIN(tb)
33         Py_XDECREF(tb->tb_next);
34         Py_XDECREF(tb->tb_frame);
35         PyObject_GC_Del(tb);
36         Py_TRASHCAN_SAFE_END(tb)
37 }
38
39 static int
40 tb_traverse(PyTracebackObject *tb, visitproc visit, void *arg)
41 {
42         Py_VISIT(tb->tb_next);
43         Py_VISIT(tb->tb_frame);
44         return 0;
45 }
46
47 static void
48 tb_clear(PyTracebackObject *tb)
49 {
50         Py_CLEAR(tb->tb_next);
51         Py_CLEAR(tb->tb_frame);
52 }
53
54 PyTypeObject PyTraceBack_Type = {
55         PyObject_HEAD_INIT(&PyType_Type)
56         0,
57         "traceback",
58         sizeof(PyTracebackObject),
59         0,
60         (destructor)tb_dealloc, /*tp_dealloc*/
61         0,              /*tp_print*/
62         (getattrfunc)tb_getattr, /*tp_getattr*/
63         0,              /*tp_setattr*/
64         0,              /*tp_compare*/
65         0,              /*tp_repr*/
66         0,              /*tp_as_number*/
67         0,              /*tp_as_sequence*/
68         0,              /*tp_as_mapping*/
69         0,              /* tp_hash */
70         0,              /* tp_call */
71         0,              /* tp_str */
72         0,              /* tp_getattro */
73         0,              /* tp_setattro */
74         0,                                      /* tp_as_buffer */
75         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
76         0,                                      /* tp_doc */
77         (traverseproc)tb_traverse,              /* tp_traverse */
78         (inquiry)tb_clear,                      /* tp_clear */
79         0,                                      /* tp_richcompare */
80         0,                                      /* tp_weaklistoffset */
81         0,                                      /* tp_iter */
82         0,                                      /* tp_iternext */
83         0,                                      /* tp_methods */
84         0,                      /* tp_members */
85         0,                      /* tp_getset */
86         0,                                      /* tp_base */
87         0,                                      /* tp_dict */
88 };
89
90 static PyTracebackObject *
91 newtracebackobject(PyTracebackObject *next, PyFrameObject *frame)
92 {
93         PyTracebackObject *tb;
94         if ((next != NULL && !PyTraceBack_Check(next)) ||
95                         frame == NULL || !PyFrame_Check(frame)) {
96                 PyErr_BadInternalCall();
97                 return NULL;
98         }
99         tb = PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
100         if (tb != NULL) {
101                 Py_XINCREF(next);
102                 tb->tb_next = next;
103                 Py_XINCREF(frame);
104                 tb->tb_frame = frame;
105                 tb->tb_lasti = frame->f_lasti;
106                 tb->tb_lineno = PyCode_Addr2Line(frame->f_code, 
107                                                  frame->f_lasti);
108                 PyObject_GC_Track(tb);
109         }
110         return tb;
111 }
112
113 int
114 PyTraceBack_Here(PyFrameObject *frame)
115 {
116         PyThreadState *tstate = PyThreadState_GET();
117         PyTracebackObject *oldtb = (PyTracebackObject *) tstate->curexc_traceback;
118         PyTracebackObject *tb = newtracebackobject(oldtb, frame);
119         if (tb == NULL)
120                 return -1;
121         tstate->curexc_traceback = (PyObject *)tb;
122         Py_XDECREF(oldtb);
123         return 0;
124 }
125
126 static int
127 tb_displayline(PyObject *f, char *filename, int lineno, char *name)
128 {
129         int err = 0;
130         FILE *xfp;
131         char linebuf[2000];
132         int i;
133         if (filename == NULL || name == NULL)
134                 return -1;
135         /* This is needed by Emacs' compile command */
136 #define FMT "  File \"%.500s\", line %d, in %.500s\n"
137         xfp = fopen(filename, "r" PY_STDIOTEXTMODE);
138         if (xfp == NULL) {
139                 /* Search tail of filename in sys.path before giving up */
140                 PyObject *path;
141                 char *tail = strrchr(filename, SEP);
142                 if (tail == NULL)
143                         tail = filename;
144                 else
145                         tail++;
146                 path = PySys_GetObject("path");
147                 if (path != NULL && PyList_Check(path)) {
148                         Py_ssize_t _npath = PyList_Size(path);
149                         int npath = Py_SAFE_DOWNCAST(_npath, Py_ssize_t, int);
150                         size_t taillen = strlen(tail);
151                         char namebuf[MAXPATHLEN+1];
152                         for (i = 0; i < npath; i++) {
153                                 PyObject *v = PyList_GetItem(path, i);
154                                 if (v == NULL) {
155                                         PyErr_Clear();
156                                         break;
157                                 }
158                                 if (PyString_Check(v)) {
159                                         size_t len;
160                                         len = PyString_GET_SIZE(v);
161                                         if (len + 1 + taillen >= MAXPATHLEN)
162                                                 continue; /* Too long */
163                                         strcpy(namebuf, PyString_AsString(v));
164                                         if (strlen(namebuf) != len)
165                                                 continue; /* v contains '\0' */
166                                         if (len > 0 && namebuf[len-1] != SEP)
167                                                 namebuf[len++] = SEP;
168                                         strcpy(namebuf+len, tail);
169                                         xfp = fopen(namebuf, "r" PY_STDIOTEXTMODE);
170                                         if (xfp != NULL) {
171                                                 filename = namebuf;
172                                                 break;
173                                         }
174                                 }
175                         }
176                 }
177         }
178         PyOS_snprintf(linebuf, sizeof(linebuf), FMT, filename, lineno, name);
179         err = PyFile_WriteString(linebuf, f);
180         if (xfp == NULL)
181                 return err;
182         else if (err != 0) {
183                 fclose(xfp);
184                 return err;
185         }
186         for (i = 0; i < lineno; i++) {
187                 char* pLastChar = &linebuf[sizeof(linebuf)-2];
188                 do {
189                         *pLastChar = '\0';
190                         if (Py_UniversalNewlineFgets(linebuf, sizeof linebuf, xfp, NULL) == NULL)
191                                 break;
192                         /* fgets read *something*; if it didn't get as
193                            far as pLastChar, it must have found a newline
194                            or hit the end of the file;  if pLastChar is \n,
195                            it obviously found a newline; else we haven't
196                            yet seen a newline, so must continue */
197                 } while (*pLastChar != '\0' && *pLastChar != '\n');
198         }
199         if (i == lineno) {
200                 char *p = linebuf;
201                 while (*p == ' ' || *p == '\t' || *p == '\014')
202                         p++;
203                 err = PyFile_WriteString("    ", f);
204                 if (err == 0) {
205                         err = PyFile_WriteString(p, f);
206                         if (err == 0 && strchr(p, '\n') == NULL)
207                                 err = PyFile_WriteString("\n", f);
208                 }
209         }
210         fclose(xfp);
211         return err;
212 }
213
214 static int
215 tb_printinternal(PyTracebackObject *tb, PyObject *f, int limit)
216 {
217         int err = 0;
218         int depth = 0;
219         PyTracebackObject *tb1 = tb;
220         while (tb1 != NULL) {
221                 depth++;
222                 tb1 = tb1->tb_next;
223         }
224         while (tb != NULL && err == 0) {
225                 if (depth <= limit) {
226                         err = tb_displayline(f,
227                             PyString_AsString(
228                                     tb->tb_frame->f_code->co_filename),
229                             tb->tb_lineno,
230                             PyString_AsString(tb->tb_frame->f_code->co_name));
231                 }
232                 depth--;
233                 tb = tb->tb_next;
234                 if (err == 0)
235                         err = PyErr_CheckSignals();
236         }
237         return err;
238 }
239
240 int
241 PyTraceBack_Print(PyObject *v, PyObject *f)
242 {
243         int err;
244         PyObject *limitv;
245         int limit = 1000;
246         if (v == NULL)
247                 return 0;
248         if (!PyTraceBack_Check(v)) {
249                 PyErr_BadInternalCall();
250                 return -1;
251         }
252         limitv = PySys_GetObject("tracebacklimit");
253         if (limitv && PyInt_Check(limitv)) {
254                 limit = PyInt_AsLong(limitv);
255                 if (limit <= 0)
256                         return 0;
257         }
258         err = PyFile_WriteString("Traceback (most recent call last):\n", f);
259         if (!err)
260                 err = tb_printinternal((PyTracebackObject *)v, f, limit);
261         return err;
262 }