ff9a03c257f7ee29618ae6dc17b7e0bb35354ddf
[profile/ivi/pulseaudio-panda.git] / src / ioline.c
1 /* $Id$ */
2
3 /***
4   This file is part of polypaudio.
5  
6   polypaudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published
8   by the Free Software Foundation; either version 2 of the License,
9   or (at your option) any later version.
10  
11   polypaudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15  
16   You should have received a copy of the GNU General Public License
17   along with polypaudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "ioline.h"
33
34 #define BUFFER_LIMIT (64*1024)
35 #define READ_SIZE (1024)
36
37 struct pa_ioline {
38     struct pa_iochannel *io;
39     int dead;
40
41     char *wbuf;
42     size_t wbuf_length, wbuf_index, wbuf_valid_length;
43
44     char *rbuf;
45     size_t rbuf_length, rbuf_index, rbuf_valid_length;
46
47     void (*callback)(struct pa_ioline*io, const char *s, void *userdata);
48     void *userdata;
49 };
50
51 static void io_callback(struct pa_iochannel*io, void *userdata);
52 static int do_write(struct pa_ioline *l);
53
54 struct pa_ioline* pa_ioline_new(struct pa_iochannel *io) {
55     struct pa_ioline *l;
56     assert(io);
57     
58     l = malloc(sizeof(struct pa_ioline));
59     assert(l);
60     l->io = io;
61     l->dead = 0;
62
63     l->wbuf = NULL;
64     l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
65
66     l->rbuf = NULL;
67     l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0;
68
69     l->callback = NULL;
70     l->userdata = NULL;
71
72     pa_iochannel_set_callback(io, io_callback, l);
73     
74     return l;
75 }
76
77 void pa_ioline_free(struct pa_ioline *l) {
78     assert(l);
79     pa_iochannel_free(l->io);
80     free(l->wbuf);
81     free(l->rbuf);
82     free(l);
83 }
84
85 void pa_ioline_puts(struct pa_ioline *l, const char *c) {
86     size_t len;
87     assert(l && c);
88     
89     len = strlen(c);
90     if (len > BUFFER_LIMIT - l->wbuf_valid_length)
91         len = BUFFER_LIMIT - l->wbuf_valid_length;
92
93     if (!len)
94         return;
95     
96     if (len > l->wbuf_length - l->wbuf_valid_length) {
97         size_t n = l->wbuf_valid_length+len;
98         char *new = malloc(n);
99         if (l->wbuf) {
100             memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
101             free(l->wbuf);
102         }
103         l->wbuf = new;
104         l->wbuf_length = n;
105         l->wbuf_index = 0;
106     } else if (len > l->wbuf_length - l->wbuf_valid_length - l->wbuf_index) {
107         memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
108         l->wbuf_index = 0;
109     }
110
111     memcpy(l->wbuf+l->wbuf_index+l->wbuf_valid_length, c, len);
112     l->wbuf_valid_length += len;
113
114     do_write(l);
115 }
116
117 void pa_ioline_set_callback(struct pa_ioline*l, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata) {
118     assert(l && callback);
119     l->callback = callback;
120     l->userdata = userdata;
121 }
122
123 static int do_read(struct pa_ioline *l) {
124     ssize_t r;
125     size_t m, len;
126     char *e;
127     assert(l);
128
129     if (!pa_iochannel_is_readable(l->io))
130         return 0;
131
132     len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
133
134     if (len < READ_SIZE) {
135         size_t n = l->rbuf_valid_length+READ_SIZE;
136
137         if (n >= BUFFER_LIMIT)
138             n = BUFFER_LIMIT;
139         
140         if (l->rbuf_length >= n) {
141             if (l->rbuf_valid_length)
142                 memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
143         } else {
144             char *new = malloc(n);
145             if (l->rbuf_valid_length)
146                 memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
147             free(l->rbuf);
148             l->rbuf = new;
149             l->rbuf_length = n;
150         }
151         
152         l->rbuf_index = 0;
153     }
154
155     len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
156     
157     if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0)
158         return -1;
159
160     e = memchr(l->rbuf+l->rbuf_index+l->rbuf_valid_length, '\n', r);
161     l->rbuf_valid_length += r;
162
163     if (!e &&l->rbuf_valid_length >= BUFFER_LIMIT)
164         e = l->rbuf+BUFFER_LIMIT-1;
165
166     if (e) {
167         char *p;
168         
169         *e = 0;
170     
171         p = l->rbuf+l->rbuf_index;
172         m = strlen(p);
173
174         l->rbuf_index += m+1;
175         l->rbuf_valid_length -= m+1;
176
177         if (l->rbuf_valid_length == 0)
178             l->rbuf_index = 0;
179
180         if (l->callback)
181             l->callback(l, p, l->userdata);
182     }
183
184     return 0;
185 }
186
187 static int do_write(struct pa_ioline *l) {
188     ssize_t r;
189     assert(l);
190
191     if (!l->wbuf_valid_length || !pa_iochannel_is_writable(l->io))
192         return 0;
193     
194     if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0)
195         return -1;
196
197     l->wbuf_valid_length -= r;
198     if (l->wbuf_valid_length == 0)
199         l->wbuf_index = 0;
200
201     return 0;
202 }
203
204 static void io_callback(struct pa_iochannel*io, void *userdata) {
205     struct pa_ioline *l = userdata;
206     assert(io && l);
207     
208     if (!l->dead && do_write(l) < 0)
209         goto fail;
210     
211     if (!l->dead && do_read(l) < 0)
212         goto fail;
213     
214     return;
215
216 fail:
217     l->dead = 1;
218     if (l->callback)
219         l->callback(l, NULL, l->userdata);
220 }