7 #include <assert.h> |
7 #include <assert.h> |
8 |
8 |
9 #include <err.h> |
9 #include <err.h> |
10 |
10 |
11 #include "sock.h" |
11 #include "sock.h" |
|
12 #include "line_proto.h" |
12 |
13 |
13 #define CONNECT_HOST "irc.fixme.fi" |
14 #define CONNECT_HOST "irc.fixme.fi" |
14 #define CONNECT_SERV "66976" |
15 #define CONNECT_SERV "6697" |
15 #define LINE_LENGTH 512 |
16 #define LINE_LENGTH 512 |
16 |
|
17 struct recvline_state { |
|
18 size_t tail_offset; |
|
19 size_t tail_len; |
|
20 }; |
|
21 |
|
22 /* |
|
23 * This looks for a full '\r\n' terminated line at the beginning of the given buffer. If found, the \r\n will be |
|
24 * replaced with a '\0', and the offset to the beginning of the next line returned. If not found, zero is returned |
|
25 * (which is never a valid next-line offset). |
|
26 * |
|
27 * The given \a hint is an hint as to the offset at which to start scanning, used for incremental invocations of this |
|
28 * on the same buffer. |
|
29 * |
|
30 */ |
|
31 int _parse_line (char *buf, size_t len, size_t *hint) { |
|
32 int i; |
|
33 |
|
34 // empty buffer -> nothing |
|
35 if (len == 0) |
|
36 return 0; |
|
37 |
|
38 // look for terminating '\r\n' sequence |
|
39 for (i = *hint; i < len - 1; i++) { |
|
40 // match this + next char |
|
41 if (buf[i] == '\r' && buf[i + 1] == '\n') |
|
42 break; |
|
43 } |
|
44 |
|
45 // incomplete? |
|
46 if (i >= len - 1) { |
|
47 *hint = len - 1; |
|
48 return 0; |
|
49 } |
|
50 |
|
51 // mangle the newline off |
|
52 buf[i] = '\0'; |
|
53 |
|
54 // return offset to next line |
|
55 return i + 2; |
|
56 } |
|
57 |
|
58 /* |
|
59 * Receive one line of data into the buffer of the given length |
|
60 */ |
|
61 int recvline (struct sock_stream *sock, char *buf, size_t len, struct recvline_state *ctx) { |
|
62 size_t recv_offset = 0, peek_offset = 0, next_offset = 0; |
|
63 int ret; |
|
64 |
|
65 // adjust offset from previous data |
|
66 recv_offset = ctx->tail_len; |
|
67 |
|
68 // move trailing data from previous line to front of buffer |
|
69 if (ctx->tail_offset) { |
|
70 // move to front |
|
71 memmove(buf, buf + ctx->tail_offset, ctx->tail_len); |
|
72 |
|
73 // reset |
|
74 ctx->tail_offset = 0; |
|
75 ctx->tail_len = 0; |
|
76 } |
|
77 |
|
78 // readline loop |
|
79 do { |
|
80 // parse any line at the beginning of the buffer |
|
81 if ((next_offset = _parse_line(buf, recv_offset, &peek_offset)) > 0) |
|
82 break; |
|
83 |
|
84 // ensure there's enough space for it |
|
85 assert(recv_offset < len); |
|
86 |
|
87 // otherwise, read more data |
|
88 if ((ret = sock_stream_read(sock, buf + recv_offset, len - recv_offset)) < 0) |
|
89 err(1, "read"); |
|
90 |
|
91 // EOF? |
|
92 if (ret == 0) |
|
93 errx(1, "read: EOF"); |
|
94 |
|
95 // update recv_offset |
|
96 recv_offset += ret; |
|
97 } while (1); |
|
98 |
|
99 // update state for next call |
|
100 ctx->tail_offset = next_offset; |
|
101 ctx->tail_len = recv_offset - next_offset; |
|
102 |
|
103 // ok |
|
104 return 0; |
|
105 } |
|
106 |
17 |
107 int main (int argc, char **argv) { |
18 int main (int argc, char **argv) { |
108 struct sock_stream *sock; |
19 struct sock_stream *sock; |
|
20 struct line_proto *lp; |
109 char line_buf[LINE_LENGTH + 1]; |
21 char line_buf[LINE_LENGTH + 1]; |
110 struct recvline_state recvline_ctx; |
|
111 struct error_info err; |
22 struct error_info err; |
112 |
23 |
113 // initialize |
24 // initialize |
114 if (sock_init(&err)) |
25 if (sock_init(&err)) |
115 errx(1, "sock_init: %s", error_msg(&err)); |
26 errx(1, "sock_init: %s", error_msg(&err)); |
116 |
27 |
117 memset(&recvline_ctx, 0, sizeof(recvline_ctx)); |
|
118 |
|
119 // over-simplified connect |
28 // over-simplified connect |
120 if (sock_gnutls_connect(&sock, CONNECT_HOST, CONNECT_SERV, &err)) |
29 if (sock_gnutls_connect(&sock, CONNECT_HOST, CONNECT_SERV, &err)) |
121 errx(1, "sock_gnutls_connect: %s", error_msg(&err)); |
30 errx(1, "sock_gnutls_connect: %s", error_msg(&err)); |
122 |
31 |
|
32 // line protocol |
|
33 if (line_proto_create(&lp, sock, &err)) |
|
34 errx(1, "line_proto_create: %s", error_msg(&err)); |
|
35 |
123 // read lines and dump them out |
36 // read lines and dump them out |
124 do { |
37 do { |
125 // recv |
38 // recv |
126 recvline(sock, line_buf, sizeof(line_buf), &recvline_ctx); |
39 if (line_proto_read(lp, line_buf, sizeof(line_buf))) |
|
40 errx(1, "line_proto_read: %s", error_msg(line_proto_error(lp))); |
127 |
41 |
128 // printf |
42 // printf |
129 printf("<<< %s\n", line_buf); |
43 printf("<<< %s\n", line_buf); |
130 |
44 |
131 } while (1); |
45 } while (1); |