help-gsasl
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 1/3] use custom getline() implementation


From: Enrico Scholz
Subject: [PATCH 1/3] use custom getline() implementation
Date: Sat, 12 Nov 2011 23:33:43 +0100

Code used getline() in a way like

| while (...) {
|   poll([fd=0], ...);
|   ...
|   getline(..., stdin);

This causes problem when a getline() call consumes multiple lines of
input. It returns only the first line (as by its definition), and
buffers the remaining data.  In the next loop, poll() will block because
it is not aware of the buffered data and waits for new one.

Unfortunately, there is no portable way to check whether a FILE contains
buffered data (and the poll() call should be skipped hence).

Patch provides a custom getline() implementation which provides
information about the buffer status.  There will be provided another
patch which integrates these changes into the poll() code.

Signed-off-by: Enrico Scholz <address@hidden>
---
 src/gsasl.c |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/src/gsasl.c b/src/gsasl.c
index 433c216..7dd2008 100644
--- a/src/gsasl.c
+++ b/src/gsasl.c
@@ -18,6 +18,8 @@
  *
  */
 
+#include <assert.h>
+
 #include "internal.h"
 #include "callbacks.h"
 #include "imap.h"
@@ -288,6 +290,108 @@ usage (int status)
   exit (status);
 }
 
+/*
+ *  |-----##########v----->
+ *        |         |
+ *      r_pos     w_pos
+ *
+ */
+struct streaminput {
+  unsigned char                buf[4096];
+  size_t               r_pos;
+  size_t               w_pos;
+  int                  fd;
+};
+
+static void streaminput_open(struct streaminput *b, int fd)
+{
+  b->r_pos = b->w_pos = 0;
+  b->fd = fd;
+}
+
+static int streaminput_have_chars(struct streaminput const *b)
+{
+  return b->r_pos < b->w_pos;
+}
+
+#define array_size(_a) (sizeof(_a)/sizeof(_a)[0])
+
+static ssize_t streaminput_getline(char **lineptr, size_t *n,
+                                  struct streaminput *b)
+{
+  unsigned char                *p = NULL;
+  size_t               out_pos = 0;
+
+  assert(b->r_pos <= b->w_pos);
+  assert(b->w_pos <= array_size(b->buf));
+
+  while (p == NULL) 
+    {
+      size_t           l = b->w_pos - b->r_pos;
+      size_t           req_len;
+
+      p = memchr(&b->buf[b->r_pos], '\n', b->w_pos - b->r_pos);
+      if (p != NULL)
+       /* we found an embedded newline... */
+       l = p - &b->buf[b->r_pos] + 1;
+
+      /* reserve xtra space for final \0 and the \r which is going to be
+        inserted by the caller */
+      req_len = out_pos + l + 2;
+      if (*n < req_len)
+       {
+         *lineptr = x2realloc(*lineptr, &req_len);
+         *n = req_len;
+       }
+
+      /* copy read data until to and inclusive the \n; when there is no \n,
+        all read data will be copied */
+      memcpy(*lineptr + out_pos, &b->buf[b->r_pos], l);
+      b->r_pos += l;
+      out_pos  += l;
+      /* not really needed, but to avoid corrupt strings in error case,
+        terminate string here */
+      lineptr[out_pos] = '\0';
+
+      assert(b->r_pos <= b->w_pos);
+      if (b->r_pos == b->w_pos)
+       {
+         /* nothing buffered; reset pointers */
+         b->r_pos = b->w_pos = 0;
+       }
+      else if (b->r_pos < array_size(b->buf) * 2 / 3)
+       {
+         /* buffered data starts in the last 2/3 of the buffer; move it to the
+            front to make operations more efficiently */
+         memmove(&b->buf[0], &b->buf[b->r_pos], b->w_pos - b->r_pos);
+         b->w_pos -= b->r_pos;
+         b->r_pos = 0;
+       }
+      assert(b->r_pos <= b->w_pos);
+      assert(b->w_pos < array_size(b->buf));
+
+      /* there was no \n in the data; we have to read new one from the input
+        stream.  */
+      if (p == NULL)
+       {
+         ssize_t               rlen = read(b->fd, &b->buf[b->w_pos],
+                                           array_size(b->buf) - b->w_pos);
+
+         if (rlen == 0)
+           /* this differs from stdio getline(3); EOF conditions will result 
in a
+              succeeded function call and return currently read data */
+           break;
+         else if (rlen < 0)
+           return -1;
+         else
+           b->w_pos += rlen;
+       }
+      assert(b->w_pos <= array_size(b->buf));
+    }
+
+  return out_pos;
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -724,10 +828,13 @@ main (int argc, char *argv[])
       if (args_info.application_data_flag)
        {
          struct pollfd pfd[2];
+         struct streaminput stdinstream;
          char *sockbuf = NULL;
          /* we read chunks of 1000 bytes at a time */
          size_t sockpos = 0, sockalloc = 0, sockalloc1 = 1000;
 
+         streaminput_open(&stdinstream, STDIN_FILENO);
+
          /* Setup pollfd structs... */
          pfd[0].fd = STDIN_FILENO;
          pfd[0].events = POLLIN;
@@ -762,10 +869,10 @@ main (int argc, char *argv[])
              if ((pfd[0].revents & (POLLIN | POLLERR)) == POLLIN)
                {
                  char *line = NULL;
-                 size_t n;
+                 size_t n = 0;
                  ssize_t len;
 
-                 len = getline (&line, &n, stdin);
+                 len = streaminput_getline (&line, &n, &stdinstream);
                  if (len <= 0)
                    break;
 
-- 
1.7.7.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]