|
8 | 8 | #include "structmember.h" |
9 | 9 | #include "osdefs.h" |
10 | 10 | #include "traceback.h" |
| 11 | +#ifdef HAVE_FCNTL_H |
| 12 | +#include <fcntl.h> |
| 13 | +#endif |
11 | 14 |
|
12 | 15 | #define OFF(x) offsetof(PyTracebackObject, x) |
13 | 16 |
|
| 17 | +/* Method from Parser/tokenizer.c */ |
| 18 | +extern char * PyTokenizer_FindEncoding(int); |
| 19 | + |
14 | 20 | static PyObject * |
15 | 21 | tb_dir(PyTracebackObject *self) |
16 | 22 | { |
@@ -128,102 +134,156 @@ PyTraceBack_Here(PyFrameObject *frame) |
128 | 134 | return 0; |
129 | 135 | } |
130 | 136 |
|
| 137 | +static int |
| 138 | +_Py_FindSourceFile(const char* filename, char* namebuf, size_t namelen, int open_flags) |
| 139 | +{ |
| 140 | + int i; |
| 141 | + int fd = -1; |
| 142 | + PyObject *v; |
| 143 | + Py_ssize_t _npath; |
| 144 | + int npath; |
| 145 | + size_t taillen; |
| 146 | + PyObject *syspath; |
| 147 | + const char* path; |
| 148 | + const char* tail; |
| 149 | + Py_ssize_t len; |
| 150 | + |
| 151 | + /* Search tail of filename in sys.path before giving up */ |
| 152 | + tail = strrchr(filename, SEP); |
| 153 | + if (tail == NULL) |
| 154 | + tail = filename; |
| 155 | + else |
| 156 | + tail++; |
| 157 | + taillen = strlen(tail); |
| 158 | + |
| 159 | + syspath = PySys_GetObject("path"); |
| 160 | + if (syspath == NULL || !PyList_Check(syspath)) |
| 161 | + return -1; |
| 162 | + _npath = PyList_Size(syspath); |
| 163 | + npath = Py_SAFE_DOWNCAST(_npath, Py_ssize_t, int); |
| 164 | + |
| 165 | + for (i = 0; i < npath; i++) { |
| 166 | + v = PyList_GetItem(syspath, i); |
| 167 | + if (v == NULL) { |
| 168 | + PyErr_Clear(); |
| 169 | + break; |
| 170 | + } |
| 171 | + if (!PyUnicode_Check(v)) |
| 172 | + continue; |
| 173 | + path = _PyUnicode_AsStringAndSize(v, &len); |
| 174 | + if (len + 1 + taillen >= (Py_ssize_t)namelen - 1) |
| 175 | + continue; /* Too long */ |
| 176 | + strcpy(namebuf, path); |
| 177 | + if (strlen(namebuf) != len) |
| 178 | + continue; /* v contains '\0' */ |
| 179 | + if (len > 0 && namebuf[len-1] != SEP) |
| 180 | + namebuf[len++] = SEP; |
| 181 | + strcpy(namebuf+len, tail); |
| 182 | + Py_BEGIN_ALLOW_THREADS |
| 183 | + fd = open(namebuf, open_flags); |
| 184 | + Py_END_ALLOW_THREADS |
| 185 | + if (0 <= fd) { |
| 186 | + return fd; |
| 187 | + } |
| 188 | + } |
| 189 | + return -1; |
| 190 | +} |
| 191 | + |
131 | 192 | int |
132 | 193 | _Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno, int indent) |
133 | 194 | { |
134 | 195 | int err = 0; |
135 | | - FILE *xfp = NULL; |
136 | | - char linebuf[2000]; |
| 196 | + int fd; |
137 | 197 | int i; |
138 | | - char namebuf[MAXPATHLEN+1]; |
| 198 | + char *found_encoding; |
| 199 | + char *encoding; |
| 200 | + PyObject *fob = NULL; |
| 201 | + PyObject *lineobj = NULL; |
| 202 | +#ifdef O_BINARY |
| 203 | + const int open_flags = O_RDONLY | O_BINARY; /* necessary for Windows */ |
| 204 | +#else |
| 205 | + const int open_flags = O_RDONLY; |
| 206 | +#endif |
| 207 | + char buf[MAXPATHLEN+1]; |
| 208 | + Py_UNICODE *u, *p; |
| 209 | + Py_ssize_t len; |
139 | 210 |
|
| 211 | + /* open the file */ |
140 | 212 | if (filename == NULL) |
141 | | - return -1; |
142 | | - xfp = fopen(filename, "r" PY_STDIOTEXTMODE); |
143 | | - if (xfp == NULL) { |
144 | | - /* Search tail of filename in sys.path before giving up */ |
145 | | - PyObject *path; |
146 | | - const char *tail = strrchr(filename, SEP); |
147 | | - if (tail == NULL) |
148 | | - tail = filename; |
149 | | - else |
150 | | - tail++; |
151 | | - path = PySys_GetObject("path"); |
152 | | - if (path != NULL && PyList_Check(path)) { |
153 | | - Py_ssize_t _npath = PyList_Size(path); |
154 | | - int npath = Py_SAFE_DOWNCAST(_npath, Py_ssize_t, int); |
155 | | - size_t taillen = strlen(tail); |
156 | | - for (i = 0; i < npath; i++) { |
157 | | - PyObject *v = PyList_GetItem(path, i); |
158 | | - if (v == NULL) { |
159 | | - PyErr_Clear(); |
160 | | - break; |
161 | | - } |
162 | | - if (PyBytes_Check(v)) { |
163 | | - size_t len; |
164 | | - len = PyBytes_GET_SIZE(v); |
165 | | - if (len + 1 + taillen >= MAXPATHLEN) |
166 | | - continue; /* Too long */ |
167 | | - strcpy(namebuf, PyBytes_AsString(v)); |
168 | | - if (strlen(namebuf) != len) |
169 | | - continue; /* v contains '\0' */ |
170 | | - if (len > 0 && namebuf[len-1] != SEP) |
171 | | - namebuf[len++] = SEP; |
172 | | - strcpy(namebuf+len, tail); |
173 | | - xfp = fopen(namebuf, "r" PY_STDIOTEXTMODE); |
174 | | - if (xfp != NULL) { |
175 | | - filename = namebuf; |
176 | | - break; |
177 | | - } |
178 | | - } |
179 | | - } |
180 | | - } |
| 213 | + return 0; |
| 214 | + Py_BEGIN_ALLOW_THREADS |
| 215 | + fd = open(filename, open_flags); |
| 216 | + Py_END_ALLOW_THREADS |
| 217 | + if (fd < 0) { |
| 218 | + fd = _Py_FindSourceFile(filename, buf, sizeof(buf), open_flags); |
| 219 | + if (fd < 0) |
| 220 | + return 0; |
| 221 | + filename = buf; |
181 | 222 | } |
182 | 223 |
|
183 | | - if (xfp == NULL) |
184 | | - return err; |
185 | | - if (err != 0) { |
186 | | - fclose(xfp); |
187 | | - return err; |
188 | | - } |
| 224 | + /* use the right encoding to decode the file as unicode */ |
| 225 | + found_encoding = PyTokenizer_FindEncoding(fd); |
| 226 | + encoding = (found_encoding != NULL) ? found_encoding : |
| 227 | + (char*)PyUnicode_GetDefaultEncoding(); |
| 228 | + lseek(fd, 0, 0); /* Reset position */ |
| 229 | + fob = PyFile_FromFd(fd, (char*)filename, "r", -1, (char*)encoding, |
| 230 | + NULL, NULL, 1); |
| 231 | + PyMem_FREE(found_encoding); |
| 232 | + if (fob == NULL) { |
| 233 | + PyErr_Clear(); |
| 234 | + close(fd); |
| 235 | + return 0; |
| 236 | + } |
189 | 237 |
|
| 238 | + /* get the line number lineno */ |
190 | 239 | for (i = 0; i < lineno; i++) { |
191 | | - char* pLastChar = &linebuf[sizeof(linebuf)-2]; |
192 | | - do { |
193 | | - *pLastChar = '\0'; |
194 | | - if (Py_UniversalNewlineFgets(linebuf, sizeof linebuf, xfp, NULL) == NULL) |
195 | | - break; |
196 | | - /* fgets read *something*; if it didn't get as |
197 | | - far as pLastChar, it must have found a newline |
198 | | - or hit the end of the file; if pLastChar is \n, |
199 | | - it obviously found a newline; else we haven't |
200 | | - yet seen a newline, so must continue */ |
201 | | - } while (*pLastChar != '\0' && *pLastChar != '\n'); |
| 240 | + Py_XDECREF(lineobj); |
| 241 | + lineobj = PyFile_GetLine(fob, -1); |
| 242 | + if (!lineobj) { |
| 243 | + err = -1; |
| 244 | + break; |
| 245 | + } |
202 | 246 | } |
203 | | - if (i == lineno) { |
204 | | - char buf[11]; |
205 | | - char *p = linebuf; |
206 | | - while (*p == ' ' || *p == '\t' || *p == '\014') |
207 | | - p++; |
208 | | - |
209 | | - /* Write some spaces before the line */ |
210 | | - strcpy(buf, " "); |
211 | | - assert (strlen(buf) == 10); |
212 | | - while (indent > 0) { |
213 | | - if(indent < 10) |
214 | | - buf[indent] = '\0'; |
215 | | - err = PyFile_WriteString(buf, f); |
216 | | - if (err != 0) |
217 | | - break; |
218 | | - indent -= 10; |
| 247 | + Py_DECREF(fob); |
| 248 | + if (!lineobj || !PyUnicode_Check(lineobj)) { |
| 249 | + Py_XDECREF(lineobj); |
| 250 | + return err; |
| 251 | + } |
| 252 | + |
| 253 | + /* remove the indentation of the line */ |
| 254 | + u = PyUnicode_AS_UNICODE(lineobj); |
| 255 | + len = PyUnicode_GET_SIZE(lineobj); |
| 256 | + for (p=u; *p == ' ' || *p == '\t' || *p == '\014'; p++) |
| 257 | + len--; |
| 258 | + if (u != p) { |
| 259 | + PyObject *truncated; |
| 260 | + truncated = PyUnicode_FromUnicode(p, len); |
| 261 | + if (truncated) { |
| 262 | + Py_DECREF(lineobj); |
| 263 | + lineobj = truncated; |
| 264 | + } else { |
| 265 | + PyErr_Clear(); |
219 | 266 | } |
| 267 | + } |
220 | 268 |
|
221 | | - if (err == 0) |
222 | | - err = PyFile_WriteString(p, f); |
223 | | - if (err == 0 && strchr(p, '\n') == NULL) |
224 | | - err = PyFile_WriteString("\n", f); |
| 269 | + /* Write some spaces before the line */ |
| 270 | + strcpy(buf, " "); |
| 271 | + assert (strlen(buf) == 10); |
| 272 | + while (indent > 0) { |
| 273 | + if(indent < 10) |
| 274 | + buf[indent] = '\0'; |
| 275 | + err = PyFile_WriteString(buf, f); |
| 276 | + if (err != 0) |
| 277 | + break; |
| 278 | + indent -= 10; |
225 | 279 | } |
226 | | - fclose(xfp); |
| 280 | + |
| 281 | + /* finally display the line */ |
| 282 | + if (err == 0) |
| 283 | + err = PyFile_WriteObject(lineobj, f, Py_PRINT_RAW); |
| 284 | + Py_DECREF(lineobj); |
| 285 | + if (err == 0) |
| 286 | + err = PyFile_WriteString("\n", f); |
227 | 287 | return err; |
228 | 288 | } |
229 | 289 |
|
|
0 commit comments