src/line_proto.c
changeset 8 be88e543c8ff
child 10 9fe218576d13
equal deleted inserted replaced
7:844f014409ff 8:be88e543c8ff
       
     1 
       
     2 #include "line_proto.h"
       
     3 
       
     4 #include <string.h>
       
     5 #include <stdlib.h>
       
     6 #include <assert.h>
       
     7 
       
     8 /*
       
     9  * Our state
       
    10  */
       
    11 struct line_proto {
       
    12     /* The sock_stream we read/write with */
       
    13     struct sock_stream *sock;
       
    14 
       
    15     /* Offset of trailing data in buf */
       
    16     size_t tail_offset;
       
    17 
       
    18     /* Length of trailing data in buf, if any */
       
    19     size_t tail_len;
       
    20 
       
    21     /* Last error */
       
    22     struct error_info err;
       
    23 };
       
    24 
       
    25 err_t line_proto_create (struct line_proto **lp_ptr, struct sock_stream *sock, struct error_info *err)
       
    26 {
       
    27     struct line_proto *lp;
       
    28 
       
    29     // allocate
       
    30     if ((lp = calloc(1, sizeof(*lp))) == NULL)
       
    31         return SET_ERROR(err, ERR_CALLOC);
       
    32 
       
    33     // store
       
    34     lp->sock = sock;
       
    35 
       
    36     // return
       
    37     *lp_ptr = lp;
       
    38 
       
    39     return SUCCESS;
       
    40 }
       
    41 
       
    42 /*
       
    43  * This looks for a full '\r\n' terminated line at the beginning of the given buffer. If found, the \r\n will be
       
    44  * replaced with a '\0', and the offset to the beginning of the next line returned. If not found, zero is returned
       
    45  * (which is never a valid next-line offset).
       
    46  *
       
    47  * The given \a hint is an hint as to the offset at which to start scanning, used for incremental invocations of this
       
    48  * on the same buffer.
       
    49  *
       
    50  */
       
    51 int _parse_line (char *buf, size_t len, size_t *hint) {
       
    52     int i;
       
    53 
       
    54     // empty buffer -> nothing
       
    55     if (len == 0)
       
    56         return 0;
       
    57 
       
    58     // look for terminating '\r\n' sequence
       
    59     for (i = *hint; i < len - 1; i++) {
       
    60         // match this + next char
       
    61         if (buf[i] == '\r' && buf[i + 1] == '\n')
       
    62             break;
       
    63     }
       
    64 
       
    65     // incomplete?
       
    66     if (i >= len - 1) {
       
    67         *hint = len - 1;
       
    68         return 0;
       
    69     }
       
    70 
       
    71     // mangle the newline off
       
    72     buf[i] = '\0';
       
    73 
       
    74     // return offset to next line
       
    75     return i + 2;
       
    76 }
       
    77 
       
    78 err_t line_proto_read (struct line_proto *lp, char *buf, size_t len)
       
    79 {
       
    80     // offset to recv() new data into, offset to _parse_line hint, offset to next line from _parse_line
       
    81     size_t recv_offset = 0, peek_offset = 0, next_offset = 0;
       
    82     int ret;
       
    83 
       
    84     // adjust offset from previous data
       
    85     recv_offset = lp->tail_len;
       
    86 
       
    87     // move trailing data from previous line to front of buffer
       
    88     if (lp->tail_offset) {
       
    89         // move to front
       
    90         memmove(buf, buf + lp->tail_offset, lp->tail_len);
       
    91 
       
    92         // reset
       
    93         lp->tail_offset = 0;
       
    94         lp->tail_len = 0;
       
    95     }
       
    96 
       
    97     // readline loop
       
    98     do {
       
    99         // parse any line at the beginning of the buffer
       
   100         if ((next_offset = _parse_line(buf, recv_offset, &peek_offset)) > 0)
       
   101             break;
       
   102 
       
   103         // ensure there's enough space for it
       
   104         assert(recv_offset < len);
       
   105         
       
   106         // otherwise, read more data
       
   107         if ((ret = sock_stream_read(lp->sock, buf + recv_offset, len - recv_offset)) < 0)
       
   108             RETURN_SET_ERROR_INFO(&lp->err, sock_stream_error(lp->sock));
       
   109 
       
   110         // EOF?
       
   111         if (ret == 0)
       
   112             return SET_ERROR(&lp->err, ERR_READ_EOF);
       
   113         
       
   114         // update recv_offset
       
   115         recv_offset += ret;
       
   116 
       
   117     } while (1);
       
   118 
       
   119     // update state for next call
       
   120     lp->tail_offset = next_offset;
       
   121     lp->tail_len = recv_offset - next_offset;
       
   122 
       
   123     // ok
       
   124     return SUCCESS;
       
   125 }
       
   126 
       
   127 const struct error_info* line_proto_error (struct line_proto *lp)
       
   128 {
       
   129     // return pointer
       
   130     return &lp->err;
       
   131 }