aboutsummaryrefslogtreecommitdiffstats
path: root/istream.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--istream.c481
1 files changed, 481 insertions, 0 deletions
diff --git a/istream.c b/istream.c
new file mode 100644
index 0000000..27b8d47
--- /dev/null
+++ b/istream.c
@@ -0,0 +1,481 @@
+/* $Id: istream.c,v 1.1 2001/11/08 05:15:57 a-ito Exp $ */
+#include "fm.h"
+#include "istream.h"
+#include <signal.h>
+
+#define uchar unsigned char
+
+#define STREAM_BUF_SIZE 8192
+#define SSL_BUF_SIZE 1536
+
+#define MUST_BE_UPDATED(bs) ((bs)->stream.cur==(bs)->stream.next)
+
+#define POP_CHAR(bs) ((bs)->iseos?'\0':(bs)->stream.buf[(bs)->stream.cur++])
+
+static void basic_close(int *handle);
+static int basic_read(int *handle, char *buf, int len);
+
+static void file_close(struct file_handle *handle);
+static int file_read(struct file_handle *handle, char *buf, int len);
+
+static int str_read(Str handle, char *buf, int len);
+
+#ifdef USE_SSL
+static void ssl_close(struct ssl_handle *handle);
+static int ssl_read(struct ssl_handle *handle, char *buf, int len);
+#endif
+
+static int ens_read(struct ens_handle *handle, char *buf, int len);
+static void ens_close(struct ens_handle *handle);
+
+static void
+do_update(BaseStream base)
+{
+ int len;
+ base->stream.cur = base->stream.next = 0;
+ len = base->read(base->handle, base->stream.buf, base->stream.size);
+ if (len <= 0)
+ base->iseos = TRUE;
+ else
+ base->stream.next += len;
+}
+
+static int
+buffer_read(StreamBuffer sb, char *obuf, int count)
+{
+ int len = sb->next - sb->cur;
+ if (len > 0) {
+ if (len > count)
+ len = count;
+ bcopy((const void *)&sb->buf[sb->cur], obuf, len);
+ sb->cur += len;
+ }
+ return len;
+}
+
+static void
+init_buffer(BaseStream base, char *buf, int bufsize)
+{
+ StreamBuffer sb = &base->stream;
+ sb->size = bufsize;
+ sb->cur = 0;
+ if (buf) {
+ sb->buf = (uchar *)buf;
+ sb->next = bufsize;
+ }
+ else {
+ sb->buf = NewAtom_N(uchar, bufsize);
+ sb->next = 0;
+ }
+ base->iseos = FALSE;
+}
+
+static void
+init_base_stream(BaseStream base, int bufsize)
+{
+ init_buffer(base, NULL, bufsize);
+}
+
+static void
+init_str_stream(BaseStream base, Str s)
+{
+ init_buffer(base, s->ptr, s->length);
+}
+
+InputStream
+newInputStream(int des)
+{
+ InputStream stream;
+ if (des < 0)
+ return NULL;
+ stream = New(union input_stream);
+ init_base_stream(&stream->base, STREAM_BUF_SIZE);
+ stream->base.type = IST_BASIC;
+ stream->base.handle = New(int);
+ *(int *)stream->base.handle = des;
+ stream->base.read = (int (*)()) basic_read;
+ stream->base.close = (void (*)()) basic_close;
+ return stream;
+}
+
+InputStream
+newFileStream(FILE *f, void (*closep)())
+{
+ InputStream stream;
+ if (f == NULL)
+ return NULL;
+ stream = New(union input_stream);
+ init_base_stream(&stream->base, STREAM_BUF_SIZE);
+ stream->file.type = IST_FILE;
+ stream->file.handle = New(struct file_handle);
+ stream->file.handle->f = f;
+ if (closep)
+ stream->file.handle->close = closep;
+ else
+ stream->file.handle->close = (void (*)()) fclose;
+ stream->file.read = (int (*)()) file_read;
+ stream->file.close = (void (*)()) file_close;
+ return stream;
+}
+
+InputStream
+newStrStream(Str s)
+{
+ InputStream stream;
+ if (s == NULL)
+ return NULL;
+ stream = New(union input_stream);
+ init_str_stream(&stream->base, s);
+ stream->str.type = IST_STR;
+ stream->str.handle = s;
+ stream->str.read = (int (*)()) str_read;
+ stream->str.close = NULL;
+ return stream;
+}
+
+#ifdef USE_SSL
+InputStream
+newSSLStream(SSL *ssl, int sock)
+{
+ InputStream stream;
+ if (sock < 0)
+ return NULL;
+ stream = New(union input_stream);
+ init_base_stream(&stream->base, SSL_BUF_SIZE);
+ stream->ssl.type = IST_SSL;
+ stream->ssl.handle = New(struct ssl_handle);
+ stream->ssl.handle->ssl = ssl;
+ stream->ssl.handle->sock = sock;
+ stream->ssl.read = (int (*)()) ssl_read;
+ stream->ssl.close = (void (*)()) ssl_close;
+ return stream;
+}
+#endif
+
+InputStream
+newEncodedStream(InputStream is, char encoding)
+{
+ InputStream stream;
+ if (is == NULL || (encoding != ENC_QUOTE && encoding != ENC_BASE64))
+ return is;
+ stream = New(union input_stream);
+ init_base_stream(&stream->base, STREAM_BUF_SIZE);
+ stream->ens.type = IST_ENCODED;
+ stream->ens.handle = New(struct ens_handle);
+ stream->ens.handle->is = is;
+ stream->ens.handle->pos = 0;
+ stream->ens.handle->encoding = encoding;
+ stream->ens.handle->s = NULL;
+ stream->ens.read = (int (*)()) ens_read;
+ stream->ens.close = (void (*)()) ens_close;
+ return stream;
+}
+
+void
+ISclose(InputStream stream)
+{
+ MySignalHandler(*prevtrap) ();
+ if (stream == NULL || stream->base.close == NULL)
+ return;
+ prevtrap = signal(SIGINT, SIG_IGN);
+ stream->base.close(stream->base.handle);
+ signal(SIGINT, prevtrap);
+}
+
+int
+ISgetc(InputStream stream)
+{
+ BaseStream base;
+ if (stream == NULL)
+ return '\0';
+ base = &stream->base;
+ if (!base->iseos && MUST_BE_UPDATED(base))
+ do_update(base);
+ return POP_CHAR(base);
+}
+
+int
+ISundogetc(InputStream stream)
+{
+ StreamBuffer sb;
+ if (stream == NULL)
+ return -1;
+ sb = &stream->base.stream;
+ if (sb->cur > 0) {
+ sb->cur--;
+ return 0;
+ }
+ return -1;
+}
+
+#define MARGIN_STR_SIZE 10
+Str
+StrISgets(InputStream stream)
+{
+ BaseStream base;
+ StreamBuffer sb;
+ Str s = NULL;
+ uchar *p;
+ int len;
+
+ if (stream == NULL)
+ return '\0';
+ base = &stream->base;
+ sb = &base->stream;
+
+ while (!base->iseos) {
+ if (MUST_BE_UPDATED(base)) {
+ do_update(base);
+ }
+ else {
+ if (p = memchr(&sb->buf[sb->cur], '\n', sb->next - sb->cur)) {
+ len = p - &sb->buf[sb->cur] + 1;
+ if (s == NULL)
+ s = Strnew_size(len);
+ Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
+ sb->cur += len;
+ return s;
+ }
+ else {
+ if (s == NULL)
+ s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
+ Strcat_charp_n(s, (char *)&sb->buf[sb->cur], sb->next - sb->cur);
+ sb->cur = sb->next;
+ }
+ }
+ }
+
+ if (s == NULL)
+ return Strnew();
+ return s;
+}
+
+Str
+StrmyISgets(InputStream stream)
+{
+ BaseStream base;
+ StreamBuffer sb;
+ Str s = NULL;
+ int i, len;
+
+ if (stream == NULL)
+ return '\0';
+ base = &stream->base;
+ sb = &base->stream;
+
+ while (!base->iseos) {
+ if (MUST_BE_UPDATED(base)) {
+ do_update(base);
+ }
+ else {
+ if (s && Strlastchar(s) == '\r') {
+ if (sb->buf[sb->cur] == '\n')
+ Strcat_char(s, (char)sb->buf[sb->cur++]);
+ return s;
+ }
+ for (i = sb->cur;
+ i < sb->next && sb->buf[i] != '\n' && sb->buf[i] != '\r';
+ i++);
+ if (i < sb->next) {
+ len = i - sb->cur + 1;
+ if (s == NULL)
+ s = Strnew_size(len + MARGIN_STR_SIZE);
+ Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
+ sb->cur = i + 1;
+ if (sb->buf[i] == '\n')
+ return s;
+ }
+ else {
+ if (s == NULL)
+ s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
+ Strcat_charp_n(s, (char *)&sb->buf[sb->cur], sb->next - sb->cur);
+ sb->cur = sb->next;
+ }
+ }
+ }
+
+ if (s == NULL)
+ return Strnew();
+ return s;
+}
+
+int
+ISread(InputStream stream, Str buf, int count)
+{
+ int rest, len;
+ BaseStream base;
+
+ if (stream == NULL || (base = &stream->base)->iseos)
+ return 0;
+
+ len = buffer_read(&base->stream, buf->ptr, count);
+ rest = count - len;
+ if (MUST_BE_UPDATED(base)) {
+ len = base->read(base->handle, &buf->ptr[len], rest);
+ if (len <= 0) {
+ base->iseos = TRUE;
+ len = 0;
+ }
+ rest -= len;
+ }
+ Strtruncate(buf, count - rest);
+ if (buf->length > 0)
+ return 1;
+ return 0;
+}
+
+int
+ISfileno(InputStream stream)
+{
+ if (stream == NULL)
+ return -1;
+ switch (IStype(stream)) {
+ case IST_BASIC:
+ return *(int *)stream->base.handle;
+ case IST_FILE:
+ return fileno(stream->file.handle->f);
+ case IST_ENCODED:
+ return ISfileno(stream->ens.handle->is);
+ default:
+ return -1;
+ }
+}
+
+int
+ISeos(InputStream stream)
+{
+ BaseStream base = &stream->base;
+ if (!base->iseos && MUST_BE_UPDATED(base))
+ do_update(base);
+ return base->iseos;
+}
+
+#ifdef USE_SSL
+Str
+ssl_get_certificate(InputStream stream)
+{
+ BIO *bp;
+ X509 *x;
+ char *p;
+ int len;
+ Str s;
+ if (stream == NULL)
+ return NULL;
+ if (IStype(stream) != IST_SSL)
+ return NULL;
+ if (stream->ssl.handle == NULL)
+ return NULL;
+ x = SSL_get_peer_certificate(stream->ssl.handle->ssl);
+ if (x == NULL)
+ return NULL;
+ bp = BIO_new(BIO_s_mem());
+ X509_print(bp, x);
+ len = (int)BIO_ctrl(bp, BIO_CTRL_INFO,0,(char *)&p);
+ s = Strnew_charp_n(p, len);
+ BIO_free_all(bp);
+ return s;
+}
+#endif
+
+/* Raw level input stream functions */
+
+static void
+basic_close(int *handle)
+{
+ close(*(int *)handle);
+}
+
+static int
+basic_read(int *handle, char *buf, int len)
+{
+ return read(*(int *)handle, buf, len);
+}
+
+static void
+file_close(struct file_handle *handle)
+{
+ handle->close(handle->f);
+}
+
+static int
+file_read(struct file_handle *handle, char *buf, int len)
+{
+ return fread(buf, 1, len, handle->f);
+}
+
+static int
+str_read(Str handle, char *buf, int len)
+{
+ return 0;
+}
+
+#ifdef USE_SSL
+static void
+ssl_close(struct ssl_handle *handle)
+{
+ close(handle->sock);
+ if (handle->ssl)
+ SSL_free(handle->ssl);
+}
+
+static int
+ssl_read(struct ssl_handle *handle, char *buf, int len)
+{
+ int status;
+ if (handle->ssl) {
+#ifdef USE_SSL_VERIFY
+ for (;;) {
+ status = SSL_read(handle->ssl, buf, len);
+ if (status > 0)
+ break;
+ switch (SSL_get_error(handle->ssl, status)) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE: /* reads can trigger write errors; see SSL_get_error(3) */
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+#else /* if !defined(USE_SSL_VERIFY) */
+ status = SSL_read(handle->ssl, buf, len);
+#endif /* !defined(USE_SSL_VERIFY) */
+ }
+ else
+ status = read(handle->sock, buf, len);
+ return status;
+}
+#endif /* USE_SSL */
+
+static void
+ens_close(struct ens_handle *handle)
+{
+ ISclose(handle->is);
+}
+
+static int
+ens_read(struct ens_handle *handle, char *buf, int len)
+{
+ if (handle->s == NULL || handle->pos == handle->s->length) {
+ char *p;
+ handle->s = StrmyISgets(handle->is);
+ if (handle->s->length == 0)
+ return 0;
+ cleanup_line(handle->s, PAGER_MODE);
+ if (handle->encoding == ENC_BASE64)
+ Strchop(handle->s);
+ p = handle->s->ptr;
+ if (handle->encoding == ENC_QUOTE)
+ handle->s = decodeQP(&p);
+ else if (handle->encoding == ENC_BASE64)
+ handle->s = decodeB(&p);
+ handle->pos = 0;
+ }
+
+ if (len > handle->s->length - handle->pos)
+ len = handle->s->length - handle->pos;
+
+ bcopy(&handle->s->ptr[handle->pos], buf, len);
+ handle->pos += len;
+ return len;
+}