summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2011-03-03 12:46:49 +0100
committerAndy Wingo <wingo@pobox.com>2011-03-03 12:57:46 +0100
commitd900843c72ee1f34d79527deb38787e581592cf5 (patch)
treec2aae2aa08a015f615b3f2fab54a488ceb7d533b
parent8d795c83d463e893cdac16733fd42bef809c0d79 (diff)
downloadguile-d900843c72ee1f34d79527deb38787e581592cf5.tar.gz
fix encoding scanning for non-seekable ports
* libguile/read.c (scm_i_scan_for_encoding): If possible, just use the read buffer for the encoding scan, and avoid seeking. Fixes `(open-input-file "/dev/urandom")', because /dev/urandom can't be seeked backwards.
-rw-r--r--libguile/read.c48
1 files changed, 41 insertions, 7 deletions
diff --git a/libguile/read.c b/libguile/read.c
index 4057e4fd0..a889133a7 100644
--- a/libguile/read.c
+++ b/libguile/read.c
@@ -1650,6 +1650,7 @@ scm_get_hash_procedure (int c)
char *
scm_i_scan_for_encoding (SCM port)
{
+ scm_t_port *pt;
char header[SCM_ENCODING_SEARCH_SIZE+1];
size_t bytes_read, encoding_length, i;
char *encoding = NULL;
@@ -1657,15 +1658,46 @@ scm_i_scan_for_encoding (SCM port)
char *pos, *encoding_start;
int in_comment;
- if (SCM_FPORTP (port) && !SCM_FDES_RANDOM_P (SCM_FPORT_FDES (port)))
- /* PORT is a non-seekable file port (e.g., as created by Bash when using
- "guile <(echo '(display "hello")')") so bail out. */
- return NULL;
+ pt = SCM_PTAB_ENTRY (port);
- bytes_read = scm_c_read (port, header, SCM_ENCODING_SEARCH_SIZE);
- header[bytes_read] = '\0';
+ if (pt->rw_active == SCM_PORT_WRITE)
+ scm_flush (port);
- scm_seek (port, scm_from_int (0), scm_from_int (SEEK_SET));
+ if (pt->rw_random)
+ pt->rw_active = SCM_PORT_READ;
+
+ if (pt->read_pos == pt->read_end)
+ {
+ /* We can use the read buffer, and thus avoid a seek. */
+ if (scm_fill_input (port) == EOF)
+ return NULL;
+
+ bytes_read = pt->read_end - pt->read_pos;
+ if (bytes_read > SCM_ENCODING_SEARCH_SIZE)
+ bytes_read = SCM_ENCODING_SEARCH_SIZE;
+
+ if (bytes_read <= 1)
+ /* An unbuffered port -- don't scan. */
+ return NULL;
+
+ memcpy (header, pt->read_pos, bytes_read);
+ header[bytes_read] = '\0';
+ }
+ else
+ {
+ /* Try to read some bytes and then seek back. Not all ports
+ support seeking back; and indeed some file ports (like
+ /dev/urandom) will succeed on an lseek (fd, 0, SEEK_CUR)---the
+ check performed by SCM_FPORT_FDES---but fail to seek
+ backwards. Hence this block comes second. We prefer to use
+ the read buffer in-place. */
+ if (SCM_FPORTP (port) && !SCM_FDES_RANDOM_P (SCM_FPORT_FDES (port)))
+ return NULL;
+
+ bytes_read = scm_c_read (port, header, SCM_ENCODING_SEARCH_SIZE);
+ header[bytes_read] = '\0';
+ scm_seek (port, scm_from_int (0), scm_from_int (SEEK_SET));
+ }
if (bytes_read > 3
&& header[0] == '\xef' && header[1] == '\xbb' && header[2] == '\xbf')
@@ -1757,6 +1789,8 @@ SCM_DEFINE (scm_file_encoding, "file-encoding", 1, 0, 0,
char *enc;
SCM s_enc;
+ SCM_VALIDATE_OPINPORT (SCM_ARG1, port);
+
enc = scm_i_scan_for_encoding (port);
if (enc == NULL)
return SCM_BOOL_F;