/* fall through */ case SEEK_CUR: if (fgetpos(fp, &pos) != 0) return -1; offset += pos; break; /* case SEEK_SET: break; */ } return fsetpos(fp, &offset);#else#error "Large file support, but no way to fseek."#endif} /* a portable ftell() function Return -1 on failure with errno set appropriately, current file position on success */static Py_off_t_portable_ftell(FILE* fp){#if !defined(HAVE_LARGEFILE_SUPPORT) return ftell(fp);#elif defined(HAVE_FTELLO) && SIZEOF_OFF_T >= 8 return ftello(fp);#elif defined(HAVE_FTELL64) return ftell64(fp);#elif SIZEOF_FPOS_T >= 8 fpos_t pos; if (fgetpos(fp, &pos) != 0) return -1; return pos;#else#error "Large file support, but no way to ftell."#endif} static PyObject *file_seek(PyFileObject *f, PyObject *args){ int whence; int ret; Py_off_t offset; PyObject *offobj; if (f->f_fp == NULL) return err_closed(); drop_readahead(f); whence = 0; if (!PyArg_ParseTuple(args, "O|i:seek", &offobj, &whence)) return NULL;#if !defined(HAVE_LARGEFILE_SUPPORT) offset = PyInt_AsLong(offobj);#else if(PyLong_Check(offobj)) offset = PyLong_AsLongLong(offobj); else offset = PyInt_AsLong(offobj);#endif if (PyErr_Occurred()) return NULL; Py_BEGIN_ALLOW_THREADS errno = 0; ret = _portable_fseek(f->f_fp, offset, whence); Py_END_ALLOW_THREADS if (ret != 0) { PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } f->f_skipnextlf = 0; Py_INCREF(Py_None); return Py_None;} #ifdef HAVE_FTRUNCATEstatic PyObject *file_truncate(PyFileObject *f, PyObject *args){ Py_off_t newsize; PyObject *newsizeobj = NULL; Py_off_t initialpos; int ret; if (f->f_fp == NULL) return err_closed(); if (!PyArg_UnpackTuple(args, "truncate", 0, 1, &newsizeobj)) return NULL; /* Get current file position. If the file happens to be open for * update and the last operation was an input operation, C doesn't * define what the later fflush() will do, but we promise truncate() * won't change the current position (and fflush() *does* change it * then at least on Windows). The easiest thing is to capture * current pos now and seek back to it at the end. */ Py_BEGIN_ALLOW_THREADS errno = 0; initialpos = _portable_ftell(f->f_fp); Py_END_ALLOW_THREADS if (initialpos == -1) goto onioerror; /* Set newsize to current postion if newsizeobj NULL, else to the * specified value. */ if (newsizeobj != NULL) {#if !defined(HAVE_LARGEFILE_SUPPORT) newsize = PyInt_AsLong(newsizeobj);#else if(PyLong_Check(newsizeobj)) newsize = PyLong_AsLongLong(newsizeobj); else newsize = PyInt_AsLong(newsizeobj);#endif if (PyErr_Occurred()) return NULL; } else /* default to current position */ newsize = initialpos; /* Flush the stream. We're mixing stream-level I/O with lower-level * I/O, and a flush may be necessary to synch both platform views * of the current file state. */ Py_BEGIN_ALLOW_THREADS errno = 0; ret = fflush(f->f_fp); Py_END_ALLOW_THREADS if (ret != 0) goto onioerror; #ifdef MS_WINDOWS /* MS _chsize doesn't work if newsize doesn't fit in 32 bits, so don't even try using it. */ { HANDLE hFile; /* Have to move current pos to desired endpoint on Windows. */ Py_BEGIN_ALLOW_THREADS errno = 0; ret = _portable_fseek(f->f_fp, newsize, SEEK_SET) != 0; Py_END_ALLOW_THREADS if (ret) goto onioerror; /* Truncate. Note that this may grow the file! */ Py_BEGIN_ALLOW_THREADS errno = 0; hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp)); ret = hFile == (HANDLE)-1; if (ret == 0) { ret = SetEndOfFile(hFile) == 0; if (ret) errno = EACCES; } Py_END_ALLOW_THREADS if (ret) goto onioerror; }#else Py_BEGIN_ALLOW_THREADS errno = 0; ret = ftruncate(fileno(f->f_fp), newsize); Py_END_ALLOW_THREADS if (ret != 0) goto onioerror;#endif /* !MS_WINDOWS */ /* Restore original file position. */ Py_BEGIN_ALLOW_THREADS errno = 0; ret = _portable_fseek(f->f_fp, initialpos, SEEK_SET) != 0; Py_END_ALLOW_THREADS if (ret) goto onioerror; Py_INCREF(Py_None); return Py_None; onioerror: PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL;}#endif /* HAVE_FTRUNCATE */ static PyObject *file_tell(PyFileObject *f){ Py_off_t pos; if (f->f_fp == NULL) return err_closed(); Py_BEGIN_ALLOW_THREADS errno = 0; pos = _portable_ftell(f->f_fp); Py_END_ALLOW_THREADS if (pos == -1) { PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } if (f->f_skipnextlf) { int c; c = GETC(f->f_fp); if (c == '\n') { pos++; f->f_skipnextlf = 0; } else if (c != EOF) ungetc(c, f->f_fp); }#if !defined(HAVE_LARGEFILE_SUPPORT) return PyInt_FromLong(pos);#else return PyLong_FromLongLong(pos);#endif} static PyObject *file_fileno(PyFileObject *f){ if (f->f_fp == NULL) return err_closed(); return PyInt_FromLong((long) fileno(f->f_fp));} static PyObject *file_flush(PyFileObject *f){ int res; if (f->f_fp == NULL) return err_closed(); Py_BEGIN_ALLOW_THREADS errno = 0; res = fflush(f->f_fp); Py_END_ALLOW_THREADS if (res != 0) { PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } Py_INCREF(Py_None); return Py_None;} static PyObject *file_isatty(PyFileObject *f){ long res; if (f->f_fp == NULL) return err_closed(); Py_BEGIN_ALLOW_THREADS res = isatty((int)fileno(f->f_fp)); Py_END_ALLOW_THREADS return PyBool_FromLong(res);} #if BUFSIZ < 8192#define SMALLCHUNK 8192#else#define SMALLCHUNK BUFSIZ#endif #if SIZEOF_INT < 4#define BIGCHUNK (512 * 32)#else#define BIGCHUNK (512 * 1024)#endif static size_tnew_buffersize(PyFileObject *f, size_t currentsize){#ifdef HAVE_FSTAT off_t pos, end; struct stat st; if (fstat(fileno(f->f_fp), &st) == 0) { end = st.st_size; /* The following is not a bug: we really need to call lseek() *and* ftell(). The reason is that some stdio libraries mistakenly flush their buffer when ftell() is called and the lseek() call it makes fails, thereby throwing away data that cannot be recovered in any way. To avoid this, we first test lseek(), and only call ftell() if lseek() works. We can't use the lseek() value either, because we need to take the amount of buffered data into account. (Yet another reason why stdio stinks. :-) */ pos = lseek(fileno(f->f_fp), 0L, SEEK_CUR); if (pos >= 0) { pos = ftell(f->f_fp); } if (pos < 0) clearerr(f->f_fp); if (end > pos && pos >= 0) return currentsize + end - pos + 1; /* Add 1 so if the file were to grow we'd notice. */ }#endif if (currentsize > SMALLCHUNK) { /* Keep doubling until we reach BIGCHUNK; then keep adding BIGCHUNK. */ if (currentsize <= BIGCHUNK) return currentsize + currentsize; else return currentsize + BIGCHUNK; } return currentsize + SMALLCHUNK;} #if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN#define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK || (x) == EAGAIN)#else#ifdef EWOULDBLOCK#define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK)#else#ifdef EAGAIN#define BLOCKED_ERRNO(x) ((x) == EAGAIN)#else#define BLOCKED_ERRNO(x) 0#endif#endif#endif static PyObject *file_read(PyFileObject *f, PyObject *args){ long bytesrequested = -1; size_t bytesread, buffersize, chunksize; PyObject *v; if (f->f_fp == NULL) return err_closed(); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && f->f_buf[0] != '\0') return err_iterbuffered(); if (!PyArg_ParseTuple(args, "|l:read", &bytesrequested)) return NULL; if (bytesrequested < 0) buffersize = new_buffersize(f, (size_t)0); else buffersize = bytesrequested; if (buffersize > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "requested number of bytes is more than a Python string can hold"); return NULL; } v = PyString_FromStringAndSize((char *)NULL, buffersize); if (v == NULL) return NULL; bytesread = 0; for (;;) { Py_BEGIN_ALLOW_THREADS errno = 0; chunksize = Py_UniversalNewlineFread(BUF(v) + bytesread, buffersize - bytesread, f->f_fp, (PyObject *)f); Py_END_ALLOW_THREADS if (chunksize == 0) { if (!ferror(f->f_fp)) break; clearerr(f->f_fp); /* When in non-blocking mode, data shouldn't * be discarded if a blocking signal was * received. That will also happen if * chunksize != 0, but bytesread < buffersize. */ if (bytesread > 0 && BLOCKED_ERRNO(errno)) break; PyErr_SetFromErrno(PyExc_IOError); Py_DECREF(v); return NULL; } bytesread += chunksize; if (bytesread < buffersize) { clearerr(f->f_fp); break; } if (bytesrequested < 0) { buffersize = new_buffersize(f, buffersize); if (_PyString_Resize(&v, buffersize) < 0) return NULL; } else { /* Got what was requested. */ break; } } if (bytesread != buffersize) _PyString_Resize(&v, bytesread); return v;} static PyObject *file_readinto(PyFileObject *f, PyObject *args){ char *ptr; Py_ssize_t ntodo; Py_ssize_t ndone, nnow; if (f->f_fp == NULL) return err_closed(); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && f->f_buf[0] != '\0') return err_iterbuffered(); if (!PyArg_ParseTuple(args, "w#", &ptr, &ntodo)) return NULL; ndone = 0; while (ntodo > 0) { Py_BEGIN_ALLOW_THREADS errno = 0; nnow = Py_UniversalNewlineFread(ptr+ndone, ntodo, f->f_fp, (PyObject *)f); Py_END_ALLOW_THREADS if (nnow == 0) { if (!ferror(f->f_fp)) break; PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } ndone += nnow; ntodo -= nnow; } return PyInt_FromSsize_t(ndone);} /**************************************************************************Routine to get next line using platform fgets(). Under MSVC 6: + MS threadsafe getc is very slow (multiple layers of function calls before+ after each character, to lock+unlock the stream).+ The stream-locking functions are MS-internal -- can't access them from user code.+ There's nothing Tim could find in the MS C or platform SDK libraries that can worm around this.+ MS fgets locks/unlocks only once per line; it's the only hook we have. So we use fgets for speed(!), despite that it's painful. MS realloc is also slow. Reports from other platforms on this method vs getc_unlocked (which MS doesn'thave): Linux a wash Solaris a wash Tru64 Unix getline_via_fgets significantly faster CAUTION: The C std isn't clear about this: in those cases where fgetswrites something into the buffer, can it write into any position beyond therequired trailing null byte? MSVC 6 fgets does not, and no platform is (yet)known on which it does; and it would be a strange way to code fgets. Still,getline_via_fgets may not work correctly if it does. The std testtest_bufio.py should fail if platform fgets() routinely writes beyond thetrailing null byte. #define DONT_USE_FGETS_IN_GETLINE to disable this code.**************************************************************************/ /* Use this routine if told to, or by default on non-get_unlocked() * platforms unless told not to. Yikes! Let's spell that out: * On a platform with getc_unlocked(): * By default, use getc_unlocked(). * If you want to use fgets() instead, #define USE_FGETS_IN_GETLINE. * On a platform without getc_unlocked(): * By default, use fgets(). * If you don't want to use fgets(), #define DONT_USE_FGETS_IN_GETLINE. */#if !defined(USE_FGETS_IN_GETLINE) && !defined(HAVE_GETC_UNLOCKED)#define USE_FGETS_IN_GETLINE#endif #if defined(DONT_USE_FGETS_IN_GETLINE) && defined(USE_FGETS_IN_GETLINE)#undef USE_FGETS_IN_GETLINE#endif #ifdef USE_FGETS_IN_GETLINEstatic PyObject*getline_via_fgets(FILE *fp){/* INITBUFSIZE is the maximum line length that lets us get away with the fast * no-realloc, one-fgets()-call path. Boosting it isn't free, because we have * to fill this much of the buffer with a known value in order to figure out * how much of the buffer fgets() overwrites. So if INITBUFSIZE is larger * than "most" lines, we waste time filling unused buffer slots. 100 is * surely adequate for most peoples' email archives, chewing over source code, * etc -- "regular old text files". * MAXBUFSIZE is the maximum line length that lets us get away with the less * fast (but still zippy) no-realloc, two-fgets()-call path. See above for * cautions about boosting that. 300 was chosen because the worst real-life * text-crunching job reported on Python-Dev was a mail-log crawler where over * half the lines were 254 chars. */#define INITBUFSIZE 100#define MAXBUFSIZE 300Lines 501–1000 of 2487. Previous lines Load more
/* fall through */ case SEEK_CUR: if (fgetpos(fp, &pos) != 0) return -1; offset += pos; break; /* case SEEK_SET: break; */ } return fsetpos(fp, &offset); #else #error "Large file support, but no way to fseek." #endif } /* a portable ftell() function Return -1 on failure with errno set appropriately, current file position on success */ static Py_off_t _portable_ftell(FILE* fp) { #if !defined(HAVE_LARGEFILE_SUPPORT) return ftell(fp); #elif defined(HAVE_FTELLO) && SIZEOF_OFF_T >= 8 return ftello(fp); #elif defined(HAVE_FTELL64) return ftell64(fp); #elif SIZEOF_FPOS_T >= 8 fpos_t pos; if (fgetpos(fp, &pos) != 0) return -1; return pos; #else #error "Large file support, but no way to ftell." #endif } static PyObject * file_seek(PyFileObject *f, PyObject *args) { int whence; int ret; Py_off_t offset; PyObject *offobj; if (f->f_fp == NULL) return err_closed(); drop_readahead(f); whence = 0; if (!PyArg_ParseTuple(args, "O|i:seek", &offobj, &whence)) return NULL; #if !defined(HAVE_LARGEFILE_SUPPORT) offset = PyInt_AsLong(offobj); #else if(PyLong_Check(offobj)) offset = PyLong_AsLongLong(offobj); else offset = PyInt_AsLong(offobj); #endif if (PyErr_Occurred()) return NULL; Py_BEGIN_ALLOW_THREADS errno = 0; ret = _portable_fseek(f->f_fp, offset, whence); Py_END_ALLOW_THREADS if (ret != 0) { PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } f->f_skipnextlf = 0; Py_INCREF(Py_None); return Py_None; } #ifdef HAVE_FTRUNCATE static PyObject * file_truncate(PyFileObject *f, PyObject *args) { Py_off_t newsize; PyObject *newsizeobj = NULL; Py_off_t initialpos; int ret; if (f->f_fp == NULL) return err_closed(); if (!PyArg_UnpackTuple(args, "truncate", 0, 1, &newsizeobj)) return NULL; /* Get current file position. If the file happens to be open for * update and the last operation was an input operation, C doesn't * define what the later fflush() will do, but we promise truncate() * won't change the current position (and fflush() *does* change it * then at least on Windows). The easiest thing is to capture * current pos now and seek back to it at the end. */ Py_BEGIN_ALLOW_THREADS errno = 0; initialpos = _portable_ftell(f->f_fp); Py_END_ALLOW_THREADS if (initialpos == -1) goto onioerror; /* Set newsize to current postion if newsizeobj NULL, else to the * specified value. */ if (newsizeobj != NULL) { #if !defined(HAVE_LARGEFILE_SUPPORT) newsize = PyInt_AsLong(newsizeobj); #else if(PyLong_Check(newsizeobj)) newsize = PyLong_AsLongLong(newsizeobj); else newsize = PyInt_AsLong(newsizeobj); #endif if (PyErr_Occurred()) return NULL; } else /* default to current position */ newsize = initialpos; /* Flush the stream. We're mixing stream-level I/O with lower-level * I/O, and a flush may be necessary to synch both platform views * of the current file state. */ Py_BEGIN_ALLOW_THREADS errno = 0; ret = fflush(f->f_fp); Py_END_ALLOW_THREADS if (ret != 0) goto onioerror; #ifdef MS_WINDOWS /* MS _chsize doesn't work if newsize doesn't fit in 32 bits, so don't even try using it. */ { HANDLE hFile; /* Have to move current pos to desired endpoint on Windows. */ Py_BEGIN_ALLOW_THREADS errno = 0; ret = _portable_fseek(f->f_fp, newsize, SEEK_SET) != 0; Py_END_ALLOW_THREADS if (ret) goto onioerror; /* Truncate. Note that this may grow the file! */ Py_BEGIN_ALLOW_THREADS errno = 0; hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp)); ret = hFile == (HANDLE)-1; if (ret == 0) { ret = SetEndOfFile(hFile) == 0; if (ret) errno = EACCES; } Py_END_ALLOW_THREADS if (ret) goto onioerror; } #else Py_BEGIN_ALLOW_THREADS errno = 0; ret = ftruncate(fileno(f->f_fp), newsize); Py_END_ALLOW_THREADS if (ret != 0) goto onioerror; #endif /* !MS_WINDOWS */ /* Restore original file position. */ Py_BEGIN_ALLOW_THREADS errno = 0; ret = _portable_fseek(f->f_fp, initialpos, SEEK_SET) != 0; Py_END_ALLOW_THREADS if (ret) goto onioerror; Py_INCREF(Py_None); return Py_None; onioerror: PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } #endif /* HAVE_FTRUNCATE */ static PyObject * file_tell(PyFileObject *f) { Py_off_t pos; if (f->f_fp == NULL) return err_closed(); Py_BEGIN_ALLOW_THREADS errno = 0; pos = _portable_ftell(f->f_fp); Py_END_ALLOW_THREADS if (pos == -1) { PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } if (f->f_skipnextlf) { int c; c = GETC(f->f_fp); if (c == '\n') { pos++; f->f_skipnextlf = 0; } else if (c != EOF) ungetc(c, f->f_fp); } #if !defined(HAVE_LARGEFILE_SUPPORT) return PyInt_FromLong(pos); #else return PyLong_FromLongLong(pos); #endif } static PyObject * file_fileno(PyFileObject *f) { if (f->f_fp == NULL) return err_closed(); return PyInt_FromLong((long) fileno(f->f_fp)); } static PyObject * file_flush(PyFileObject *f) { int res; if (f->f_fp == NULL) return err_closed(); Py_BEGIN_ALLOW_THREADS errno = 0; res = fflush(f->f_fp); Py_END_ALLOW_THREADS if (res != 0) { PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * file_isatty(PyFileObject *f) { long res; if (f->f_fp == NULL) return err_closed(); Py_BEGIN_ALLOW_THREADS res = isatty((int)fileno(f->f_fp)); Py_END_ALLOW_THREADS return PyBool_FromLong(res); } #if BUFSIZ < 8192 #define SMALLCHUNK 8192 #else #define SMALLCHUNK BUFSIZ #endif #if SIZEOF_INT < 4 #define BIGCHUNK (512 * 32) #else #define BIGCHUNK (512 * 1024) #endif static size_t new_buffersize(PyFileObject *f, size_t currentsize) { #ifdef HAVE_FSTAT off_t pos, end; struct stat st; if (fstat(fileno(f->f_fp), &st) == 0) { end = st.st_size; /* The following is not a bug: we really need to call lseek() *and* ftell(). The reason is that some stdio libraries mistakenly flush their buffer when ftell() is called and the lseek() call it makes fails, thereby throwing away data that cannot be recovered in any way. To avoid this, we first test lseek(), and only call ftell() if lseek() works. We can't use the lseek() value either, because we need to take the amount of buffered data into account. (Yet another reason why stdio stinks. :-) */ pos = lseek(fileno(f->f_fp), 0L, SEEK_CUR); if (pos >= 0) { pos = ftell(f->f_fp); } if (pos < 0) clearerr(f->f_fp); if (end > pos && pos >= 0) return currentsize + end - pos + 1; /* Add 1 so if the file were to grow we'd notice. */ } #endif if (currentsize > SMALLCHUNK) { /* Keep doubling until we reach BIGCHUNK; then keep adding BIGCHUNK. */ if (currentsize <= BIGCHUNK) return currentsize + currentsize; else return currentsize + BIGCHUNK; } return currentsize + SMALLCHUNK; } #if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN #define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK || (x) == EAGAIN) #else #ifdef EWOULDBLOCK #define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK) #else #ifdef EAGAIN #define BLOCKED_ERRNO(x) ((x) == EAGAIN) #else #define BLOCKED_ERRNO(x) 0 #endif #endif #endif static PyObject * file_read(PyFileObject *f, PyObject *args) { long bytesrequested = -1; size_t bytesread, buffersize, chunksize; PyObject *v; if (f->f_fp == NULL) return err_closed(); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && f->f_buf[0] != '\0') return err_iterbuffered(); if (!PyArg_ParseTuple(args, "|l:read", &bytesrequested)) return NULL; if (bytesrequested < 0) buffersize = new_buffersize(f, (size_t)0); else buffersize = bytesrequested; if (buffersize > PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "requested number of bytes is more than a Python string can hold"); return NULL; } v = PyString_FromStringAndSize((char *)NULL, buffersize); if (v == NULL) return NULL; bytesread = 0; for (;;) { Py_BEGIN_ALLOW_THREADS errno = 0; chunksize = Py_UniversalNewlineFread(BUF(v) + bytesread, buffersize - bytesread, f->f_fp, (PyObject *)f); Py_END_ALLOW_THREADS if (chunksize == 0) { if (!ferror(f->f_fp)) break; clearerr(f->f_fp); /* When in non-blocking mode, data shouldn't * be discarded if a blocking signal was * received. That will also happen if * chunksize != 0, but bytesread < buffersize. */ if (bytesread > 0 && BLOCKED_ERRNO(errno)) break; PyErr_SetFromErrno(PyExc_IOError); Py_DECREF(v); return NULL; } bytesread += chunksize; if (bytesread < buffersize) { clearerr(f->f_fp); break; } if (bytesrequested < 0) { buffersize = new_buffersize(f, buffersize); if (_PyString_Resize(&v, buffersize) < 0) return NULL; } else { /* Got what was requested. */ break; } } if (bytesread != buffersize) _PyString_Resize(&v, bytesread); return v; } static PyObject * file_readinto(PyFileObject *f, PyObject *args) { char *ptr; Py_ssize_t ntodo; Py_ssize_t ndone, nnow; if (f->f_fp == NULL) return err_closed(); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && f->f_buf[0] != '\0') return err_iterbuffered(); if (!PyArg_ParseTuple(args, "w#", &ptr, &ntodo)) return NULL; ndone = 0; while (ntodo > 0) { Py_BEGIN_ALLOW_THREADS errno = 0; nnow = Py_UniversalNewlineFread(ptr+ndone, ntodo, f->f_fp, (PyObject *)f); Py_END_ALLOW_THREADS if (nnow == 0) { if (!ferror(f->f_fp)) break; PyErr_SetFromErrno(PyExc_IOError); clearerr(f->f_fp); return NULL; } ndone += nnow; ntodo -= nnow; } return PyInt_FromSsize_t(ndone); } /************************************************************************** Routine to get next line using platform fgets(). Under MSVC 6: + MS threadsafe getc is very slow (multiple layers of function calls before+ after each character, to lock+unlock the stream). + The stream-locking functions are MS-internal -- can't access them from user code. + There's nothing Tim could find in the MS C or platform SDK libraries that can worm around this. + MS fgets locks/unlocks only once per line; it's the only hook we have. So we use fgets for speed(!), despite that it's painful. MS realloc is also slow. Reports from other platforms on this method vs getc_unlocked (which MS doesn't have): Linux a wash Solaris a wash Tru64 Unix getline_via_fgets significantly faster CAUTION: The C std isn't clear about this: in those cases where fgets writes something into the buffer, can it write into any position beyond the required trailing null byte? MSVC 6 fgets does not, and no platform is (yet) known on which it does; and it would be a strange way to code fgets. Still, getline_via_fgets may not work correctly if it does. The std test test_bufio.py should fail if platform fgets() routinely writes beyond the trailing null byte. #define DONT_USE_FGETS_IN_GETLINE to disable this code. **************************************************************************/ /* Use this routine if told to, or by default on non-get_unlocked() * platforms unless told not to. Yikes! Let's spell that out: * On a platform with getc_unlocked(): * By default, use getc_unlocked(). * If you want to use fgets() instead, #define USE_FGETS_IN_GETLINE. * On a platform without getc_unlocked(): * By default, use fgets(). * If you don't want to use fgets(), #define DONT_USE_FGETS_IN_GETLINE. */ #if !defined(USE_FGETS_IN_GETLINE) && !defined(HAVE_GETC_UNLOCKED) #define USE_FGETS_IN_GETLINE #endif #if defined(DONT_USE_FGETS_IN_GETLINE) && defined(USE_FGETS_IN_GETLINE) #undef USE_FGETS_IN_GETLINE #endif #ifdef USE_FGETS_IN_GETLINE static PyObject* getline_via_fgets(FILE *fp) { /* INITBUFSIZE is the maximum line length that lets us get away with the fast * no-realloc, one-fgets()-call path. Boosting it isn't free, because we have * to fill this much of the buffer with a known value in order to figure out * how much of the buffer fgets() overwrites. So if INITBUFSIZE is larger * than "most" lines, we waste time filling unused buffer slots. 100 is * surely adequate for most peoples' email archives, chewing over source code, * etc -- "regular old text files". * MAXBUFSIZE is the maximum line length that lets us get away with the less * fast (but still zippy) no-realloc, two-fgets()-call path. See above for * cautions about boosting that. 300 was chosen because the worst real-life * text-crunching job reported on Python-Dev was a mail-log crawler where over * half the lines were 254 chars. */ #define INITBUFSIZE 100 #define MAXBUFSIZE 300