Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-stream-fs.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-stream-fs.c : file system based stream */
3
4 /*
5  * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
6  *          Michael Zucchi <notzed@ximian.com>
7  *
8  * Copyright 1999-2003 Ximian, Inc. (www.ximian.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <errno.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #include <glib.h>
36 #include <glib/gstdio.h>
37
38 #include "camel-file-utils.h"
39 #include "camel-operation.h"
40 #include "camel-private.h"
41 #include "camel-stream-fs.h"
42
43 static CamelSeekableStreamClass *parent_class = NULL;
44
45 /* Returns the class for a CamelStreamFS */
46 #define CSFS_CLASS(so) CAMEL_STREAM_FS_CLASS (CAMEL_OBJECT_GET_CLASS(so))
47
48 static ssize_t stream_read   (CamelStream *stream, char *buffer, size_t n);
49 static ssize_t stream_write  (CamelStream *stream, const char *buffer, size_t n);
50 static int stream_flush  (CamelStream *stream);
51 static int stream_close  (CamelStream *stream);
52 static off_t stream_seek (CamelSeekableStream *stream, off_t offset,
53                           CamelStreamSeekPolicy policy);
54
55 static void
56 camel_stream_fs_class_init (CamelStreamFsClass *camel_stream_fs_class)
57 {
58         CamelSeekableStreamClass *camel_seekable_stream_class =
59                 CAMEL_SEEKABLE_STREAM_CLASS (camel_stream_fs_class);
60         CamelStreamClass *camel_stream_class =
61                 CAMEL_STREAM_CLASS (camel_stream_fs_class);
62
63         parent_class = CAMEL_SEEKABLE_STREAM_CLASS (camel_type_get_global_classfuncs (camel_seekable_stream_get_type ()));
64
65         /* virtual method overload */
66         camel_stream_class->read = stream_read;
67         camel_stream_class->write = stream_write;
68         camel_stream_class->flush = stream_flush;
69         camel_stream_class->close = stream_close;
70
71         camel_seekable_stream_class->seek = stream_seek;
72 }
73
74 static void
75 camel_stream_fs_init (gpointer object, gpointer klass)
76 {
77         CamelStreamFs *stream = CAMEL_STREAM_FS (object);
78
79         stream->fd = -1;
80         ((CamelSeekableStream *)stream)->bound_end = CAMEL_STREAM_UNBOUND;
81 }
82
83 static void
84 camel_stream_fs_finalize (CamelObject *object)
85 {
86         CamelStreamFs *stream_fs = CAMEL_STREAM_FS (object);
87
88         if (stream_fs->fd != -1)
89                 close (stream_fs->fd);
90 }
91
92
93 CamelType
94 camel_stream_fs_get_type (void)
95 {
96         static CamelType camel_stream_fs_type = CAMEL_INVALID_TYPE;
97
98         if (camel_stream_fs_type == CAMEL_INVALID_TYPE) {
99                 camel_stream_fs_type = camel_type_register (camel_seekable_stream_get_type (), "CamelStreamFs",
100                                                             sizeof (CamelStreamFs),
101                                                             sizeof (CamelStreamFsClass),
102                                                             (CamelObjectClassInitFunc) camel_stream_fs_class_init,
103                                                             NULL,
104                                                             (CamelObjectInitFunc) camel_stream_fs_init,
105                                                             (CamelObjectFinalizeFunc) camel_stream_fs_finalize);
106         }
107
108         return camel_stream_fs_type;
109 }
110
111 /**
112  * camel_stream_fs_new_with_fd:
113  * @fd: a file descriptor
114  *
115  * Creates a new fs stream using the given file descriptor @fd as the
116  * backing store. When the stream is destroyed, the file descriptor
117  * will be closed.
118  *
119  * Returns a new #CamelStreamFs
120  **/
121 CamelStream *
122 camel_stream_fs_new_with_fd (int fd)
123 {
124         CamelStreamFs *stream_fs;
125         off_t offset;
126
127         if (fd == -1)
128                 return NULL;
129
130         stream_fs = CAMEL_STREAM_FS (camel_object_new (camel_stream_fs_get_type ()));
131         stream_fs->fd = fd;
132         offset = lseek (fd, 0, SEEK_CUR);
133         if (offset == -1)
134                 offset = 0;
135         CAMEL_SEEKABLE_STREAM (stream_fs)->position = offset;
136
137         return CAMEL_STREAM (stream_fs);
138 }
139
140 /**
141  * camel_stream_fs_new_with_fd_and_bounds:
142  * @fd: a file descriptor
143  * @start: the first valid position in the file
144  * @end: the first invalid position in the file, or #CAMEL_STREAM_UNBOUND
145  *
146  * Gets a stream associated with the given file descriptor and bounds.
147  * When the stream is destroyed, the file descriptor will be closed.
148  *
149  * Returns the bound stream
150  **/
151 CamelStream *
152 camel_stream_fs_new_with_fd_and_bounds (int fd, off_t start, off_t end)
153 {
154         CamelStream *stream;
155
156         stream = camel_stream_fs_new_with_fd (fd);
157         camel_seekable_stream_set_bounds (CAMEL_SEEKABLE_STREAM (stream), start, end);
158
159         return stream;
160 }
161
162 /**
163  * camel_stream_fs_new_with_name:
164  * @name: a local filename
165  * @flags: flags as in open(2)
166  * @mode: a file mode
167  *
168  * Creates a new #CamelStreamFs corresponding to the named file, flags,
169  * and mode.
170  *
171  * Returns the new stream, or %NULL on error.
172  **/
173 CamelStream *
174 camel_stream_fs_new_with_name (const char *name, int flags, mode_t mode)
175 {
176         int fd;
177
178         fd = g_open (name, flags|O_BINARY, mode);
179         if (fd == -1) {
180                 return NULL;
181         }
182
183         return camel_stream_fs_new_with_fd (fd);
184 }
185
186 /**
187  * camel_stream_fs_new_with_name_and_bounds:
188  * @name: a local filename
189  * @flags: flags as in open(2)
190  * @mode: a file mode
191  * @start: the first valid position in the file
192  * @end: the first invalid position in the file, or #CAMEL_STREAM_UNBOUND
193  *
194  * Creates a new CamelStream corresponding to the given arguments.
195  *
196  * Returns the stream, or %NULL on error.
197  **/
198 CamelStream *
199 camel_stream_fs_new_with_name_and_bounds (const char *name, int flags,
200                                           mode_t mode, off_t start, off_t end)
201 {
202         CamelStream *stream;
203
204         stream = camel_stream_fs_new_with_name (name, flags, mode);
205         if (stream == NULL)
206                 return NULL;
207
208         camel_seekable_stream_set_bounds (CAMEL_SEEKABLE_STREAM (stream),
209                                           start, end);
210
211         return stream;
212 }
213
214
215 static ssize_t
216 stream_read (CamelStream *stream, char *buffer, size_t n)
217 {
218         CamelStreamFs *stream_fs = CAMEL_STREAM_FS (stream);
219         CamelSeekableStream *seekable = CAMEL_SEEKABLE_STREAM (stream);
220         ssize_t nread;
221         
222         if (seekable->bound_end != CAMEL_STREAM_UNBOUND)
223                 n = MIN (seekable->bound_end - seekable->position, n);
224         
225         if ((nread = camel_read (stream_fs->fd, buffer, n)) > 0)
226                 seekable->position += nread;
227         else if (nread == 0)
228                 stream->eos = TRUE;
229         
230         return nread;
231 }
232
233 static ssize_t
234 stream_write (CamelStream *stream, const char *buffer, size_t n)
235 {
236         CamelStreamFs *stream_fs = CAMEL_STREAM_FS (stream);
237         CamelSeekableStream *seekable = CAMEL_SEEKABLE_STREAM (stream);
238         ssize_t nwritten;
239         
240         if (seekable->bound_end != CAMEL_STREAM_UNBOUND)
241                 n = MIN (seekable->bound_end - seekable->position, n);
242         
243         if ((nwritten = camel_write (stream_fs->fd, buffer, n)) > 0)
244                 seekable->position += nwritten;
245         
246         return nwritten;
247 }
248
249 static int
250 stream_flush (CamelStream *stream)
251 {
252         return fsync(((CamelStreamFs *)stream)->fd);
253 }
254
255 static int
256 stream_close (CamelStream *stream)
257 {
258         if (close (((CamelStreamFs *)stream)->fd) == -1)
259                 return -1;
260         
261         ((CamelStreamFs *)stream)->fd = -1;
262         return 0;
263 }
264
265 static off_t
266 stream_seek (CamelSeekableStream *stream, off_t offset, CamelStreamSeekPolicy policy)
267 {
268         CamelStreamFs *stream_fs = CAMEL_STREAM_FS (stream);
269         off_t real = 0;
270
271         switch (policy) {
272         case CAMEL_STREAM_SET:
273                 real = offset;
274                 break;
275         case CAMEL_STREAM_CUR:
276                 real = stream->position + offset;
277                 break;
278         case CAMEL_STREAM_END:
279                 if (stream->bound_end == CAMEL_STREAM_UNBOUND) {
280                         real = lseek(stream_fs->fd, offset, SEEK_END);
281                         if (real != -1) {
282                                 if (real<stream->bound_start)
283                                         real = stream->bound_start;
284                                 stream->position = real;
285                         }
286                         return real;
287                 }
288                 real = stream->bound_end + offset;
289                 break;
290         }
291
292         if (stream->bound_end != CAMEL_STREAM_UNBOUND)
293                 real = MIN (real, stream->bound_end);
294         real = MAX (real, stream->bound_start);
295
296         real = lseek(stream_fs->fd, real, SEEK_SET);
297         if (real == -1)
298                 return -1;
299
300         if (real != stream->position && ((CamelStream *)stream)->eos)
301                 ((CamelStream *)stream)->eos = FALSE;
302
303         stream->position = real;
304
305         return real;
306 }