commit 65ff9ee6981de43290ad37826173fa49e766b379
Author: Oswald Buddenhagen <o...@kde.org>
Date:   Sun Apr 3 18:47:37 2011 +0200

    decouple the filling of the read buffer from consuming it
    
    this prepares the code for being called from a callback.
    
    notably, this makes the imap list parser have a "soft stack", so the
    recursion can be suspended at any time.

 src/drv_imap.c |  143 ++++++++++++++++++++++++++++++------------------
 src/isync.h    |   11 ++--
 src/socket.c   |  108 ++++++++++++++++++------------------
 3 files changed, 150 insertions(+), 112 deletions(-)

diff --git a/src/drv_imap.c b/src/drv_imap.c
index 10d5d35..3bbee11 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -67,6 +67,13 @@ typedef struct _list {
        int len;
 } list_t;
 
+#define MAX_LIST_DEPTH 5
+
+typedef struct parse_list_state {
+       list_t *head, **stack[MAX_LIST_DEPTH];
+       int level, need_bytes;
+} parse_list_state_t;
+
 struct imap_cmd;
 
 typedef struct imap_store {
@@ -78,6 +85,7 @@ typedef struct imap_store {
        list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
        message_t **msgapp; /* FETCH results */
        unsigned caps; /* CAPABILITY results */
+       parse_list_state_t parse_list_sts;
        /* command queue */
        int nexttag, num_in_progress, literal_pending;
        struct imap_cmd *in_progress, **in_progress_append;
@@ -430,66 +438,76 @@ free_list( list_t *list )
        }
 }
 
+enum {
+       LIST_OK,
+       LIST_PARTIAL,
+       LIST_BAD
+};
+
 static int
-parse_imap_list_l( imap_store_t *ctx, char **sp, list_t **curp, int level )
+parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
 {
-       list_t *cur;
+       list_t *cur, **curp;
        char *s = *sp, *p;
-       int n, bytes;
+       int bytes;
+
+       assert( sts );
+       assert( sts->level > 0 );
+       curp = sts->stack[--sts->level];
+       bytes = sts->need_bytes;
+       if (bytes >= 0) {
+               sts->need_bytes = -1;
+               if (!bytes)
+                       goto getline;
+               cur = (list_t *)((char *)curp - offsetof(list_t, next));
+               s = cur->val + cur->len - bytes;
+               goto getbytes;
+       }
 
        for (;;) {
                while (isspace( (unsigned char)*s ))
                        s++;
-               if (level && *s == ')') {
+               if (sts->level && *s == ')') {
                        s++;
-                       break;
+                       curp = sts->stack[--sts->level];
+                       goto next;
                }
                *curp = cur = nfmalloc( sizeof(*cur) );
-               curp = &cur->next;
                cur->val = 0; /* for clean bail */
+               curp = &cur->next;
+               *curp = 0; /* ditto */
                if (*s == '(') {
                        /* sublist */
+                       if (sts->level == MAX_LIST_DEPTH)
+                               goto bail;
                        s++;
                        cur->val = LIST;
-                       if (parse_imap_list_l( ctx, &s, &cur->child, level + 1 
))
-                               goto bail;
+                       sts->stack[sts->level++] = curp;
+                       curp = &cur->child;
+                       *curp = 0; /* for clean bail */
+                       goto next2;
                } else if (ctx && *s == '{') {
                        /* literal */
                        bytes = cur->len = strtol( s + 1, &s, 10 );
-                       if (*s != '}')
+                       if (*s != '}' || *++s)
                                goto bail;
 
                        s = cur->val = nfmalloc( cur->len );
 
-                       /* dump whats left over in the input buffer */
-                       n = ctx->conn.bytes - ctx->conn.offset;
-
-                       if (n > bytes)
-                               /* the entire message fit in the buffer */
-                               n = bytes;
-
-                       memcpy( s, ctx->conn.buf + ctx->conn.offset, n );
-                       s += n;
-                       bytes -= n;
+                 getbytes:
+                       bytes -= socket_read( &ctx->conn, s, bytes );
+                       if (bytes > 0)
+                               goto postpone;
 
-                       /* mark that we used part of the buffer */
-                       ctx->conn.offset += n;
-
-                       /* now read the rest of the message */
-                       while (bytes > 0) {
-                               if ((n = socket_read( &ctx->conn, s, bytes )) 
<= 0)
-                                       goto bail;
-                               s += n;
-                               bytes -= n;
-                       }
                        if (DFlags & XVERBOSE) {
                                puts( "=========" );
                                fwrite( cur->val, cur->len, 1, stdout );
                                puts( "=========" );
                        }
 
-                       if (buffer_gets( &ctx->conn, &s ))
-                               goto bail;
+                 getline:
+                       if (!(s = socket_read_line( &ctx->conn )))
+                               goto postpone;
                } else if (*s == '"') {
                        /* quoted string */
                        s++;
@@ -506,7 +524,7 @@ parse_imap_list_l( imap_store_t *ctx, char **sp, list_t 
**curp, int level )
                        /* atom */
                        p = s;
                        for (; *s && !isspace( (unsigned char)*s ); s++)
-                               if (level && *s == ')')
+                               if (sts->level && *s == ')')
                                        break;
                        cur->len = s - p;
                        if (cur->len == 3 && !memcmp ("NIL", p, 3))
@@ -518,41 +536,50 @@ parse_imap_list_l( imap_store_t *ctx, char **sp, list_t 
**curp, int level )
                        }
                }
 
-               if (!level)
+         next:
+               if (!sts->level)
                        break;
+         next2:
                if (!*s)
                        goto bail;
        }
        *sp = s;
-       *curp = 0;
-       return 0;
+       return LIST_OK;
 
+  postpone:
+       if (sts->level < MAX_LIST_DEPTH) {
+               sts->stack[sts->level++] = curp;
+               sts->need_bytes = bytes;
+               return LIST_PARTIAL;
+       }
   bail:
-       *curp = 0;
-       return -1;
+       free_list( sts->head );
+       return LIST_BAD;
 }
 
-static list_t *
-parse_imap_list( imap_store_t *ctx, char **sp )
+static void
+parse_list_init( parse_list_state_t *sts )
 {
-       list_t *head;
-
-       if (!parse_imap_list_l( ctx, sp, &head, 0 ))
-               return head;
-       free_list( head );
-       return NULL;
+       sts->need_bytes = -1;
+       sts->level = 1;
+       sts->head = 0;
+       sts->stack[0] = &sts->head;
 }
 
 static list_t *
 parse_list( char **sp )
 {
-       return parse_imap_list( 0, sp );
+       parse_list_state_t sts;
+       parse_list_init( &sts );
+       if (parse_imap_list( 0, sp, &sts ) == LIST_OK)
+               return sts.head;
+       return NULL;
 }
 
 static int
-parse_fetch( imap_store_t *ctx, char *cmd ) /* move this down */
+parse_fetch( imap_store_t *ctx, list_t *list )
 {
-       list_t *tmp, *list, *flags;
+       list_t *tmp, *flags;
        char *body = 0;
        imap_message_t *cur;
        msg_data_t *msgdata;
@@ -560,8 +587,6 @@ parse_fetch( imap_store_t *ctx, char *cmd ) /* move this 
down */
        int uid = 0, mask = 0, status = 0, size = 0;
        unsigned i;
 
-       list = parse_imap_list( ctx, &cmd );
-
        if (!is_list( list )) {
                error( "IMAP error: bogus FETCH response\n" );
                free_list( list );
@@ -781,8 +806,11 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
 
        greeted = ctx->greeting;
        for (;;) {
-               if (buffer_gets( &ctx->conn, &cmd ))
-                       break;
+               if (!(cmd = socket_read_line( &ctx->conn ))) {
+                       if (socket_fill( &ctx->conn ) < 0)
+                               break;
+                       continue;
+               }
 
                arg = next_arg( &cmd );
                if (*arg == '*') {
@@ -817,8 +845,17 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
                                else if (!strcmp( "RECENT", arg1 ))
                                        ctx->gen.recent = atoi( arg );
                                else if(!strcmp ( "FETCH", arg1 )) {
-                                       if (parse_fetch( ctx, cmd ))
+                                       parse_list_init( &ctx->parse_list_sts );
+                                 do_fetch:
+                                       if ((resp = parse_imap_list( ctx, &cmd, 
&ctx->parse_list_sts )) == LIST_BAD)
                                                break; /* stream is likely to 
be useless now */
+                                       if (resp == LIST_PARTIAL) {
+                                               if (socket_fill( &ctx->conn ) < 
0)
+                                                       break;
+                                               goto do_fetch;
+                                       }
+                                       if (parse_fetch( ctx, 
ctx->parse_list_sts.head ) < 0)
+                                               break; /* this may mean 
anything, so prefer not to spam the log */
                                }
                        } else {
                                error( "IMAP error: unrecognized untagged 
response '%s'\n", arg );
diff --git a/src/isync.h b/src/isync.h
index 865e297..b605fff 100644
--- a/src/isync.h
+++ b/src/isync.h
@@ -79,8 +79,9 @@ typedef struct {
        SSL *ssl;
 #endif
 
-       int bytes;
-       int offset;
+       int offset; /* start of filled bytes in buffer */
+       int bytes; /* number of filled bytes in buffer */
+       int scanoff; /* offset to continue scanning for newline at, relative to 
'offset' */
        char buf[1024];
 } conn_t;
 
@@ -335,13 +336,13 @@ extern const char *Home;
 int socket_connect( const server_conf_t *conf, conn_t *sock );
 int socket_start_tls( const server_conf_t *conf, conn_t *sock );
 void socket_close( conn_t *sock );
-int socket_read( conn_t *sock, char *buf, int len );
+int socket_fill( conn_t *sock );
+int socket_read( conn_t *sock, char *buf, int len ); /* never waits */
+char *socket_read_line( conn_t *sock ); /* don't free return value; never 
waits */
 typedef enum { KeepOwn = 0, GiveOwn } ownership_t;
 int socket_write( conn_t *sock, char *buf, int len, ownership_t takeOwn );
 int socket_pending( conn_t *sock );
 
-int buffer_gets( conn_t *b, char **s );
-
 void cram( const char *challenge, const char *user, const char *pass,
            char **_final, int *_finallen );
 
diff --git a/src/socket.c b/src/socket.c
index 9bf47d3..6941226 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -354,11 +354,17 @@ socket_close( conn_t *sock )
 }
 
 int
-socket_read( conn_t *sock, char *buf, int len )
+socket_fill( conn_t *sock )
 {
-       int n;
-
+       char *buf;
+       int n = sock->offset + sock->bytes;
+       int len = sizeof(sock->buf) - n;
+       if (!len) {
+               error( "Socket error: receive buffer full. Probably protocol 
error.\n" );
+               return -1;
+       }
        assert( sock->fd >= 0 );
+       buf = sock->buf + n;
        n =
 #ifdef HAVE_LIBSSL
                sock->ssl ? SSL_read( sock->ssl, buf, len ) :
@@ -368,10 +374,55 @@ socket_read( conn_t *sock, char *buf, int len )
                socket_perror( "read", sock, n );
                close( sock->fd );
                sock->fd = -1;
+               return -1;
+       } else {
+               sock->bytes += n;
+               return 0;
        }
+}
+
+int
+socket_read( conn_t *conn, char *buf, int len )
+{
+       int n = conn->bytes;
+       if (n > len)
+               n = len;
+       memcpy( buf, conn->buf + conn->offset, n );
+       if (!(conn->bytes -= n))
+               conn->offset = 0;
+       else
+               conn->offset += n;
        return n;
 }
 
+char *
+socket_read_line( conn_t *b )
+{
+       char *p, *s;
+       int n;
+
+       s = b->buf + b->offset;
+       p = memchr( s + b->scanoff, '\n', b->bytes - b->scanoff );
+       if (!p) {
+               b->scanoff = b->bytes;
+               if (b->offset + b->bytes == sizeof(b->buf)) {
+                       memmove( b->buf, b->buf + b->offset, b->bytes );
+                       b->offset = 0;
+               }
+               return 0;
+       }
+       n = p + 1 - s;
+       b->offset += n;
+       b->bytes -= n;
+       b->scanoff = 0;
+       if (p != s && p[-1] == '\r')
+               p--;
+       *p = 0;
+       if (DFlags & VERBOSE)
+               puts( s );
+       return s;
+}
+
 int
 socket_write( conn_t *sock, char *buf, int len, ownership_t takeOwn )
 {
@@ -410,57 +461,6 @@ socket_pending( conn_t *sock )
        return 0;
 }
 
-/* simple line buffering */
-int
-buffer_gets( conn_t *b, char **s )
-{
-       int n;
-       int start = b->offset;
-
-       *s = b->buf + start;
-
-       for (;;) {
-               /* make sure we have enough data to read the \r\n sequence */
-               if (b->offset + 1 >= b->bytes) {
-                       if (start) {
-                               /* shift down used bytes */
-                               *s = b->buf;
-
-                               assert( start <= b->bytes );
-                               n = b->bytes - start;
-
-                               if (n)
-                                       memmove( b->buf, b->buf + start, n );
-                               b->offset -= start;
-                               b->bytes = n;
-                               start = 0;
-                       }
-
-                       n = socket_read( b, b->buf + b->bytes,
-                                        sizeof(b->buf) - b->bytes );
-
-                       if (n <= 0)
-                               return -1;
-
-                       b->bytes += n;
-               }
-
-               if (b->buf[b->offset] == '\r') {
-                       assert( b->offset + 1 < b->bytes );
-                       if (b->buf[b->offset + 1] == '\n') {
-                               b->buf[b->offset] = 0;  /* terminate the string 
*/
-                               b->offset += 2; /* next line */
-                               if (DFlags & VERBOSE)
-                                       puts( *s );
-                               return 0;
-                       }
-               }
-
-               b->offset++;
-       }
-       /* not reached */
-}
-
 #ifdef HAVE_LIBSSL
 /* this isn't strictly socket code, but let's have all OpenSSL use in one 
file. */
 

------------------------------------------------------------------------------
Xperia(TM) PLAY
It's a major breakthrough. An authentic gaming
smartphone on the nation's most reliable network.
And it wants your games.
http://p.sf.net/sfu/verizon-sfdev
_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to