static int freadptrbufsize (FILE *fp) { size_t size = 0; freadptr (fp, &size); return size; }
ssize_t getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax, int delim1, int delim2, FILE *stream) { size_t nbytes_avail; /* Allocated but unused bytes in *LINEPTR. */ char *read_pos; /* Where we're reading into *LINEPTR. */ ssize_t bytes_stored = -1; char *ptr = *lineptr; size_t size = *linesize; bool found_delimiter; if (!ptr) { size = nmax < MIN_CHUNK ? nmax : MIN_CHUNK; ptr = malloc (size); if (!ptr) return -1; } if (size < offset) goto done; nbytes_avail = size - offset; read_pos = ptr + offset; if (nbytes_avail == 0 && nmax <= size) goto done; /* Normalize delimiters, since memchr2 doesn't handle EOF. */ if (delim1 == EOF) delim1 = delim2; else if (delim2 == EOF) delim2 = delim1; flockfile (stream); found_delimiter = false; do { /* Here always ptr + size == read_pos + nbytes_avail. Also nbytes_avail > 0 || size < nmax. */ int c IF_LINT (= 0); const char *buffer; size_t buffer_len; buffer = freadptr (stream, &buffer_len); if (buffer) { if (delim1 != EOF) { const char *end = memchr2 (buffer, delim1, delim2, buffer_len); if (end) { buffer_len = end - buffer + 1; found_delimiter = true; } } } else { c = getc (stream); if (c == EOF) { /* Return partial line, if any. */ if (read_pos == ptr) goto unlock_done; else break; } if (c == delim1 || c == delim2) found_delimiter = true; buffer_len = 1; } /* We always want at least one byte left in the buffer, since we always (unless we get an error while reading the first byte) NUL-terminate the line buffer. */ if (nbytes_avail < buffer_len + 1 && size < nmax) { /* Grow size proportionally, not linearly, to avoid O(n^2) running time. */ size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size; char *newptr; /* Increase newsize so that it becomes >= (read_pos - ptr) + buffer_len. */ if (newsize - (read_pos - ptr) < buffer_len + 1) newsize = (read_pos - ptr) + buffer_len + 1; /* Respect nmax. This handles possible integer overflow. */ if (! (size < newsize && newsize <= nmax)) newsize = nmax; if (GETNDELIM2_MAXIMUM < newsize - offset) { size_t newsizemax = offset + GETNDELIM2_MAXIMUM + 1; if (size == newsizemax) goto unlock_done; newsize = newsizemax; } nbytes_avail = newsize - (read_pos - ptr); newptr = realloc (ptr, newsize); if (!newptr) goto unlock_done; ptr = newptr; size = newsize; read_pos = size - nbytes_avail + ptr; } /* Here, if size < nmax, nbytes_avail >= buffer_len + 1. If size == nmax, nbytes_avail > 0. */ if (1 < nbytes_avail) { size_t copy_len = nbytes_avail - 1; if (buffer_len < copy_len) copy_len = buffer_len; if (buffer) memcpy (read_pos, buffer, copy_len); else *read_pos = c; read_pos += copy_len; nbytes_avail -= copy_len; } /* Here still nbytes_avail > 0. */ if (buffer && freadseek (stream, buffer_len)) goto unlock_done; } while (!found_delimiter); /* Done - NUL terminate and return the number of bytes read. At this point we know that nbytes_avail >= 1. */ *read_pos = '\0'; bytes_stored = read_pos - (ptr + offset); unlock_done: funlockfile (stream); done: *lineptr = ptr; *linesize = size; return bytes_stored ? bytes_stored : -1; }
int freadseek (FILE *fp, size_t offset) { size_t total_buffered; int fd; if (offset == 0) return 0; /* Seek over the already read and buffered input as quickly as possible, without doing any system calls. */ total_buffered = freadahead (fp); /* This loop is usually executed at most twice: once for ungetc buffer (if present) and once for the main buffer. */ while (total_buffered > 0) { size_t buffered; if (freadptr (fp, &buffered) != NULL && buffered > 0) { size_t increment = (buffered < offset ? buffered : offset); freadptrinc (fp, increment); offset -= increment; if (offset == 0) return 0; total_buffered -= increment; if (total_buffered == 0) break; } /* Read one byte. If we were reading from the ungetc buffer, this switches the stream back to the main buffer. */ if (fgetc (fp) == EOF) goto eof; offset--; if (offset == 0) return 0; total_buffered--; } /* Test whether the stream is seekable or not. */ fd = fileno (fp); if (fd >= 0 && lseek (fd, 0, SEEK_CUR) >= 0) { /* FP refers to a regular file. fseek is most efficient in this case. */ return fseeko (fp, offset, SEEK_CUR); } else { /* FP is a non-seekable stream, possibly not even referring to a file descriptor. Read OFFSET bytes explicitly and discard them. */ char buf[4096]; do { size_t count = (sizeof (buf) < offset ? sizeof (buf) : offset); if (fread (buf, 1, count, fp) < count) goto eof; offset -= count; } while (offset > 0); return 0; } eof: /* EOF, or error before or while reading. */ if (ferror (fp)) return EOF; else /* Encountered EOF. */ return 0; }
int main (int argc, char **argv) { int nbytes = atoi (argv[1]); void *buf = malloc (nbytes); ASSERT (fread (buf, 1, nbytes, stdin) == nbytes); if (lseek (0, 0, SEEK_CUR) == nbytes) { /* An unbuffered stdio, such as BeOS or on uClibc compiled without __STDIO_BUFFERS. Or stdin is a pipe. */ size_t size; ASSERT (freadptr (stdin, &size) == NULL); } else { /* Normal buffered stdio. */ const char stdin_contents[] = "#!/bin/sh\n\n./test-freadptr${EXEEXT} 5 < \"$srcdir/test-freadptr.sh\" || exit 1\ncat \"$srcdir/test-freadptr.sh\" | ./test-freadptr${EXEEXT} 5 || exit 1\nexit 0\n"; const char *expected = stdin_contents + nbytes; size_t available1; size_t available2; size_t available3; /* Test normal behaviour. */ { const char *ptr = freadptr (stdin, &available1); ASSERT (ptr != NULL); ASSERT (available1 != 0); ASSERT (available1 <= strlen (expected)); ASSERT (memcmp (ptr, expected, available1) == 0); } /* Test behaviour after normal ungetc. */ ungetc (fgetc (stdin), stdin); { const char *ptr = freadptr (stdin, &available2); if (ptr != NULL) { ASSERT (available2 == available1); ASSERT (memcmp (ptr, expected, available2) == 0); } } /* Test behaviour after arbitrary ungetc. */ fgetc (stdin); ungetc ('@', stdin); { const char *ptr = freadptr (stdin, &available3); if (ptr != NULL) { ASSERT (available3 == 1 || available3 == available1); ASSERT (ptr[0] == '@'); if (available3 > 1) { ASSERT (memcmp (ptr + 1, expected + 1, available3 - 1) == 0); } } } } return 0; }