ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-socket-buffer.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-socket-buffer.c
3  *
4  * Copyright (C) 2001 Seth Nickell
5  * Copyright (C) 2001 Maciej Stachowiak
6  *
7  * The Gnome Library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * The Gnome Library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA. 
21  *
22  */
23 /*
24  * Authors: Seth Nickell <snickell@stanford.edu>
25  *          Maciej Stachowiak <mjs@noisehavoc.org>
26  *          (reverse-engineered from code by Ian McKellar <yakk@yakk.net>)
27  */
28
29 #include <config.h>
30 #include "gnome-vfs-socket-buffer.h"
31
32 #include <string.h>
33 #include <glib/gmem.h>
34 #include <glib/gmessages.h>
35
36 \f
37 #define BUFFER_SIZE 4096
38
39 struct Buffer {
40         gchar data[BUFFER_SIZE];
41         guint offset;
42         guint byte_count;
43         GnomeVFSResult last_error;
44 };
45 typedef struct Buffer Buffer;
46
47
48 struct  GnomeVFSSocketBuffer {
49         GnomeVFSSocket *socket;
50         Buffer input_buffer;
51         Buffer output_buffer;
52 };
53
54 \f
55 static void
56 buffer_init (Buffer *buffer)
57 {
58         buffer->byte_count = 0;
59         buffer->offset = 0;
60         buffer->last_error = GNOME_VFS_OK;
61 }
62
63 /**
64  * gnome_vfs_socket_buffer_new:
65  * @socket: socket to be buffered
66  *
67  * Create a socket buffer around @socket. A buffered
68  * socket allows data to be poked at without reading it
69  * as it will be buffered. A future read will retrieve
70  * the data again.
71  *
72  * Return value: a newly allocated GnomeVFSSocketBuffer
73  **/
74 GnomeVFSSocketBuffer*  
75 gnome_vfs_socket_buffer_new (GnomeVFSSocket *socket)
76 {
77         GnomeVFSSocketBuffer *socket_buffer;
78
79         g_return_val_if_fail (socket != NULL, NULL);
80
81         socket_buffer = g_new (GnomeVFSSocketBuffer, 1);
82         socket_buffer->socket = socket;
83
84         buffer_init (&socket_buffer->input_buffer);
85         buffer_init (&socket_buffer->output_buffer);
86
87         return socket_buffer;
88 }
89
90 /**
91  * gnome_vfs_socket_buffer_destroy:
92  * @socket_buffer: buffered socket to destray
93  * @close_socket: if %TRUE the socket being buffered will be closed too
94  *
95  * Free the socket buffer.
96  *
97  * Return value: GnomeVFSResult indicating the success of the operation
98  **/
99 GnomeVFSResult   
100 gnome_vfs_socket_buffer_destroy  (GnomeVFSSocketBuffer *socket_buffer, 
101                                   gboolean close_socket)
102 {
103         gnome_vfs_socket_buffer_flush (socket_buffer);
104
105         if (close_socket) {
106                 gnome_vfs_socket_close (socket_buffer->socket);
107         }
108         g_free (socket_buffer);
109         return GNOME_VFS_OK;
110 }
111
112
113
114
115 static gboolean
116 refill_input_buffer (GnomeVFSSocketBuffer *socket_buffer)
117 {
118         Buffer *input_buffer;
119         GnomeVFSResult result;
120         GnomeVFSFileSize bytes_read;
121         
122         input_buffer = &socket_buffer->input_buffer;
123
124         if (input_buffer->last_error != GNOME_VFS_OK
125             || input_buffer->byte_count > 0) {
126                 return FALSE;
127         }
128
129         input_buffer->offset = 0;
130
131         result = gnome_vfs_socket_read (socket_buffer->socket,
132                                         &input_buffer->data, 
133                                         BUFFER_SIZE,
134                                         &bytes_read);
135         
136         if (bytes_read == 0) {
137                 input_buffer->last_error = GNOME_VFS_ERROR_EOF;
138                 return FALSE;
139         }
140
141         input_buffer->byte_count = bytes_read;
142
143         return TRUE;
144 }
145
146 /**
147  * gnome_vfs_socket_buffer_read:
148  * @socket_buffer: buffered socket to read data from
149  * @buffer: allocated buffer of at least @bytes bytes to be read into
150  * @bytes: number of bytes to read from @socket into @socket_buffer
151  * @bytes_read: pointer to a GnomeVFSFileSize, will contain
152  * the number of bytes actually read from the socket on return.
153  *
154  * Read @bytes bytes of data from the @socket into @socket_buffer.
155  *
156  * Return value: GnomeVFSResult indicating the success of the operation
157  **/
158 GnomeVFSResult   
159 gnome_vfs_socket_buffer_read (GnomeVFSSocketBuffer *socket_buffer,
160                               gpointer buffer,
161                               GnomeVFSFileSize bytes,
162                               GnomeVFSFileSize *bytes_read)
163 {
164         Buffer *input_buffer;
165         GnomeVFSResult result;
166         GnomeVFSFileSize n;
167         
168         g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
169         g_return_val_if_fail (buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
170         
171         /* Quote from UNIX 98:
172            "If nbyte is 0, read() will return 0 and have no other results."
173         */
174         if (bytes == 0) {
175                 *bytes_read = 0;
176                 return GNOME_VFS_OK;
177         }
178                 
179         input_buffer = &socket_buffer->input_buffer;
180
181         result = GNOME_VFS_OK;
182
183         if (input_buffer->byte_count == 0) {
184                 if (! refill_input_buffer (socket_buffer)) {
185                         /* The buffer is empty but we had an error last time we
186                            filled it, so we report the error.  */
187                         result = input_buffer->last_error;
188                         input_buffer->last_error = GNOME_VFS_OK;
189                 }
190         }
191
192         if (input_buffer->byte_count != 0) {
193                 n = MIN (bytes, input_buffer->byte_count);
194                 memcpy (buffer, input_buffer->data + input_buffer->offset, n);
195                 input_buffer->byte_count -= n;
196                 input_buffer->offset += n;
197                 if (bytes_read != NULL) {
198                         *bytes_read = n;
199                 }
200         } else {
201                 if (bytes_read != NULL) {
202                         *bytes_read = 0;
203                 }
204         }
205
206         if (result == GNOME_VFS_ERROR_EOF) {
207                 result = GNOME_VFS_OK;
208         }
209
210         return result;
211 }
212
213 /**
214  * gnome_vfs_socket_buffer_peekc:
215  * @socket_buffer: the socket buffer to read from
216  * @character: pointer to a char, will contain a character on return from
217  * a successful "peek"
218  *
219  * Peek at the next character in @socket_buffer without actually reading
220  * the character in. The next read will retrieve @c (as well as any following
221  * data if requested).
222  *
223  * Return value: GnomeVFSResult indicating the success of the operation
224  **/
225 GnomeVFSResult
226 gnome_vfs_socket_buffer_peekc (GnomeVFSSocketBuffer *socket_buffer,
227                                gchar *character)
228 {
229         GnomeVFSResult result;
230         Buffer *input_buffer;
231
232         g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
233         g_return_val_if_fail (character != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
234
235         input_buffer = &socket_buffer->input_buffer;
236         result = GNOME_VFS_OK;
237
238         if (input_buffer->byte_count == 0) {
239                 if (!refill_input_buffer (socket_buffer)) {
240                         /* The buffer is empty but we had an error last time we
241                            filled it, so we report the error.  */
242                         result = input_buffer->last_error;
243                         input_buffer->last_error = GNOME_VFS_OK;
244                 }
245         }
246
247         if (result == GNOME_VFS_OK) {
248                 *character = *(input_buffer->data + input_buffer->offset);
249         }
250
251         return result;
252 }
253
254
255
256 static GnomeVFSResult
257 flush (GnomeVFSSocketBuffer *socket_buffer)
258 {
259         Buffer *output_buffer;
260         GnomeVFSResult result;
261         GnomeVFSFileSize bytes_written;
262
263         output_buffer = &socket_buffer->output_buffer;
264
265         while (output_buffer->byte_count > 0) {
266                 result = gnome_vfs_socket_write (socket_buffer->socket, 
267                                                  output_buffer->data,
268                                                  output_buffer->byte_count,
269                                                  &bytes_written);
270                 output_buffer->last_error = result;
271                 output_buffer->byte_count -= bytes_written;
272         }
273
274         return GNOME_VFS_OK;
275 }
276
277 /**
278  * gnome_vfs_socket_buffer_write:
279  * @socket_buffer: buffered socket to write data to
280  * @buffer: data to write to the socket
281  * @bytes: number of bytes from @buffer to write to @socket_buffer
282  * @bytes_written: pointer to a GnomeVFSFileSize, will contain
283  * the number of bytes actually written to the socket on return.
284  *
285  * Write @bytes bytes of data from @buffer to @socket_buffer.
286  *
287  * Return value: GnomeVFSResult indicating the success of the operation
288  **/ 
289 GnomeVFSResult   
290 gnome_vfs_socket_buffer_write (GnomeVFSSocketBuffer *socket_buffer, 
291                                gconstpointer buffer,
292                                GnomeVFSFileSize bytes,
293                                GnomeVFSFileSize *bytes_written)
294 {
295         Buffer *output_buffer;
296         GnomeVFSFileSize write_count;
297         GnomeVFSResult result;
298         const gchar *p;
299
300         g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
301         g_return_val_if_fail (buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
302         g_return_val_if_fail (bytes_written != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
303
304         output_buffer = &socket_buffer->output_buffer;
305
306         result = GNOME_VFS_OK;
307
308         p = buffer;
309         write_count = 0;
310         while (write_count < bytes) {
311                 if (output_buffer->byte_count < BUFFER_SIZE) {
312                         GnomeVFSFileSize n;
313
314                         n = MIN (BUFFER_SIZE - output_buffer->byte_count,
315                                  bytes);
316                         memcpy (output_buffer->data + output_buffer->byte_count,
317                                 p, n);
318                         p += n;
319                         write_count += n;
320                         output_buffer->byte_count += n;
321                 } else {
322                         result = flush (socket_buffer);
323                         if (result != GNOME_VFS_OK) {
324                                 break;
325                         }
326                 }
327         }
328
329         if (bytes_written != NULL) {
330                 *bytes_written = write_count;
331         }
332                 
333         return result;
334 }
335
336 /**
337  * gnome_vfs_socket_buffer_flush:
338  * @socket_buffer: buffer to flush
339  *
340  * Write all outstanding data to @socket_buffer.
341  *
342  * Return value: GnomeVFSResult indicating the success of the operation
343  **/
344 GnomeVFSResult   
345 gnome_vfs_socket_buffer_flush (GnomeVFSSocketBuffer *socket_buffer)
346 {
347         g_return_val_if_fail (socket_buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
348
349         return flush (socket_buffer);
350 }
351
352
353