fd1039d67b7fb69c044d0f942c1ed71f3d14e71b
[silc.git] / apps / irssi / src / core / net-sendbuffer.c
1 /*
2  net-sendbuffer.c : Buffered send()
3
4     Copyright (C) 1998-2000 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22
23 #include "network.h"
24 #include "net-sendbuffer.h"
25
26 struct _NET_SENDBUF_REC {
27         GIOChannel *handle;
28
29         int send_tag;
30         int bufsize;
31         int bufpos;
32         char *buffer; /* Buffer is NULL until it's actually needed. */
33 };
34
35 static GSList *buffers;
36
37 /* Create new buffer - if `bufsize' is zero or less, DEFAULT_BUFFER_SIZE
38    is used */
39 NET_SENDBUF_REC *net_sendbuffer_create(GIOChannel *handle, int bufsize)
40 {
41         NET_SENDBUF_REC *rec;
42
43         g_return_val_if_fail(handle != NULL, NULL);
44
45         rec = g_new0(NET_SENDBUF_REC, 1);
46         rec->send_tag = -1;
47         rec->handle = handle;
48         rec->bufsize = bufsize > 0 ? bufsize : DEFAULT_BUFFER_SIZE;
49
50         buffers = g_slist_append(buffers, rec);
51         return rec;
52 }
53
54 /* Destroy the buffer. `close' specifies if socket handle should be closed. */
55 void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close)
56 {
57         buffers = g_slist_remove(buffers, rec);
58
59         if (rec->send_tag != -1) g_source_remove(rec->send_tag);
60         if (close) net_disconnect(rec->handle);
61         g_free_not_null(rec->buffer);
62         g_free(rec);
63 }
64
65 /* Transmit all data from buffer - return TRUE if the whole buffer was sent */
66 static int buffer_send(NET_SENDBUF_REC *rec)
67 {
68         int ret;
69
70         ret = net_transmit(rec->handle, rec->buffer, rec->bufpos);
71         if (ret < 0 || rec->bufpos == ret) {
72                 /* error/all sent - don't try to send it anymore */
73                 g_free_and_null(rec->buffer);
74                 return TRUE;
75         }
76
77         if (ret > 0) {
78                 rec->bufpos -= ret;
79                 g_memmove(rec->buffer, rec->buffer+ret, rec->bufpos);
80         }
81         return FALSE;
82 }
83
84 static void sig_sendbuffer(NET_SENDBUF_REC *rec)
85 {
86         if (rec->buffer != NULL) {
87                 if (!buffer_send(rec))
88                         return;
89         }
90
91         g_source_remove(rec->send_tag);
92         rec->send_tag = -1;
93 }
94
95 /* Add `data' to transmit buffer - return FALSE if buffer is full */
96 static int buffer_add(NET_SENDBUF_REC *rec, const void *data, int size)
97 {
98         if (rec->buffer == NULL) {
99                 rec->buffer = g_malloc(rec->bufsize);
100                 rec->bufpos = 0;
101         }
102
103         if (rec->bufpos+size > rec->bufsize)
104                 return FALSE;
105
106         memcpy(rec->buffer+rec->bufpos, data, size);
107         rec->bufpos += size;
108         return TRUE;
109 }
110
111 /* Send data, if all of it couldn't be sent immediately, it will be resent
112    automatically after a while. Returns -1 if some unrecoverable error
113    occured. */
114 int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size)
115 {
116         int ret;
117
118         g_return_val_if_fail(rec != NULL, -1);
119         g_return_val_if_fail(data != NULL, -1);
120         if (size <= 0) return 0;
121
122         if (rec->buffer == NULL) {
123                 /* nothing in buffer - transmit immediately */
124                 ret = net_transmit(rec->handle, data, size);
125                 if (ret < 0) return -1;
126                 size -= ret;
127                 data = ((const char *) data) + ret;
128         }
129
130         if (size <= 0)
131                 return 0;
132
133         /* everything couldn't be sent. */
134         if (rec->send_tag == -1) {
135                 rec->send_tag =
136                         g_input_add(rec->handle, G_INPUT_WRITE,
137                                     (GInputFunction) sig_sendbuffer, rec);
138         }
139
140         return buffer_add(rec, data, size) ? 0 : -1;
141 }
142
143 /* Flush the buffer, blocks until finished. */
144 void net_sendbuffer_flush(NET_SENDBUF_REC *rec)
145 {
146         int handle;
147
148         if (rec->buffer == NULL)
149                 return;
150
151         /* set the socket blocking while doing this */
152         handle = g_io_channel_unix_get_fd(rec->handle);
153 #ifndef WIN32
154         fcntl(handle, F_SETFL, 0);
155 #endif
156         while (!buffer_send(rec)) ;
157 #ifndef WIN32
158         fcntl(handle, F_SETFL, O_NONBLOCK);
159 #endif
160 }
161
162 /* Returns the socket handle */
163 GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec)
164 {
165         g_return_val_if_fail(rec != NULL, NULL);
166
167         return rec->handle;
168 }
169
170 void net_sendbuffer_init(void)
171 {
172         buffers = NULL;
173 }
174
175 void net_sendbuffer_deinit(void)
176 {
177 }