"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-input-textline.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-input-textline.c: textline based input
4  *
5  * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
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 Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 #include <gsf-config.h>
23 #include <gsf/gsf-input-textline.h>
24 #include <gsf/gsf-input-impl.h>
25 #include <gsf/gsf-impl-utils.h>
26
27 #include <string.h>
28
29 static GObjectClass *parent_class;
30
31 struct _GsfInputTextline {
32         GsfInput input;
33
34         GsfInput        *source;
35         guint8 const    *remainder;
36         unsigned         remainder_size;
37         unsigned         max_line_size;
38
39         unsigned char   *buf;
40         unsigned         buf_size;
41
42         /* int           current_line; */
43 };
44
45 typedef struct {
46         GsfInputClass input_class;
47 } GsfInputTextlineClass;
48
49 /**
50  * gsf_input_textline_new :
51  * @source : in some combination of ascii and utf8
52  *
53  * <note>This adds a reference to @source.</note>
54  *
55  * Returns: a new file or %NULL.
56  **/
57 GsfInput *
58 gsf_input_textline_new (GsfInput *source)
59 {
60         GsfInputTextline *input;
61
62         g_return_val_if_fail (source != NULL, NULL);
63
64         input = g_object_new (GSF_INPUT_TEXTLINE_TYPE, NULL);
65         if (G_UNLIKELY (NULL == input)) return NULL;
66
67         g_object_ref (G_OBJECT (source));
68         input->source = source;
69         input->buf  = NULL;
70         input->buf_size = 0;
71         gsf_input_set_size (GSF_INPUT (input), gsf_input_size (source));
72         gsf_input_set_name (GSF_INPUT (input), gsf_input_name (source));
73
74         return GSF_INPUT (input);
75 }
76
77 static void
78 gsf_input_textline_finalize (GObject *obj)
79 {
80         GsfInputTextline *input = (GsfInputTextline *)obj;
81
82         if (input->source != NULL) {
83                 g_object_unref (G_OBJECT (input->source));
84                 input->source = NULL;
85         }
86         g_free (input->buf);
87         input->buf  = NULL;
88         input->buf_size = 0;
89
90         parent_class->finalize (obj);
91 }
92
93 static GsfInput *
94 gsf_input_textline_dup (GsfInput *src_input, G_GNUC_UNUSED GError **err)
95 {
96         GsfInputTextline const *src = (GsfInputTextline *)src_input;
97         GsfInputTextline *dst = g_object_new (GSF_INPUT_TEXTLINE_TYPE, NULL);
98         if (G_UNLIKELY (NULL == dst)) return NULL;
99
100         dst->source = src->source;
101         g_object_ref (G_OBJECT (dst->source));
102         gsf_input_set_size (GSF_INPUT (dst), gsf_input_size (src_input));
103
104         return GSF_INPUT (dst);
105 }
106
107 static guint8 const *
108 gsf_input_textline_read (GsfInput *input, size_t num_bytes, guint8 *buffer)
109 {
110         GsfInputTextline *textline = GSF_INPUT_TEXTLINE (input);
111         textline->remainder = NULL;
112         return gsf_input_read (textline->source, num_bytes, buffer);
113 }
114
115 static gboolean
116 gsf_input_textline_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
117 {
118         GsfInputTextline *textline = GSF_INPUT_TEXTLINE (input);
119         textline->remainder = NULL;
120         return gsf_input_seek (textline->source, offset, whence);
121 }
122
123 static void
124 gsf_input_textline_init (GObject *obj)
125 {
126         GsfInputTextline *textline = GSF_INPUT_TEXTLINE (obj);
127
128         textline->source         = NULL;
129         textline->remainder      = NULL;
130         textline->remainder_size = 0;
131         textline->max_line_size  = 512; /* an initial guess */
132         textline->buf            = NULL;
133         textline->buf_size       = 0;
134 }
135
136 static void
137 gsf_input_textline_class_init (GObjectClass *gobject_class)
138 {
139         GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
140
141         gobject_class->finalize = gsf_input_textline_finalize;
142         input_class->Dup        = gsf_input_textline_dup;
143         input_class->Read       = gsf_input_textline_read;
144         input_class->Seek       = gsf_input_textline_seek;
145
146         parent_class = g_type_class_peek_parent (gobject_class);
147 }
148
149 GSF_CLASS (GsfInputTextline, gsf_input_textline,
150            gsf_input_textline_class_init, gsf_input_textline_init,
151            GSF_INPUT_TYPE)
152
153 /**
154  * gsf_input_textline_ascii_gets :
155  * @textline : #GsfInputTextline
156  *
157  * A utility routine to read things line by line from the underlying source.
158  * Trailing newlines and carriage returns are stripped, and the resultant buffer
159  * can be edited.
160  *
161  * Returns: the string read, or %NULL on eof.
162  **/
163 unsigned char *
164 gsf_input_textline_ascii_gets (GsfInputTextline *textline)
165 {
166         guint8 const *ptr ,*end;
167         gsf_off_t remain;
168         unsigned len, count = 0;
169
170         g_return_val_if_fail (textline != NULL, NULL);
171
172         while (1) {
173                 if (textline->remainder == NULL ||
174                     textline->remainder_size == 0) {
175                         remain = gsf_input_remaining (textline->source);
176                         len = MIN (remain, textline->max_line_size);
177
178                         textline->remainder = gsf_input_read (textline->source, len, NULL);
179                         if (textline->remainder == NULL)
180                                 return NULL;
181                         textline->remainder_size = len;
182                 }
183
184                 ptr = textline->remainder;
185                 end = ptr + textline->remainder_size;
186                 for (; ptr < end ; ptr++)
187                         if (*ptr == '\n' || *ptr == '\r')
188                                 break;
189
190                 /* copy the remains into the buffer, grow it if necessary */
191                 len = ptr - textline->remainder;
192                 if (count + len >= textline->buf_size) {
193                         textline->buf_size += len;
194                         textline->buf = g_renew (guint8, textline->buf,
195                                                  textline->buf_size + 1);
196                 }
197
198                 g_return_val_if_fail (textline->buf != NULL, NULL);
199
200                 memcpy (textline->buf + count, textline->remainder, len);
201                 count += len;
202
203                 if (ptr < end) {
204                         unsigned char last = ptr [0];
205
206                         /* eat the trailing new line */
207                         ptr++;
208                         if (ptr >= end) {
209                                 /* be extra careful, the newline is at the bound */
210                                 if (gsf_input_remaining (textline->source) > 0) {
211                                         ptr = gsf_input_read (textline->source, 1, NULL);
212                                         if (ptr == NULL)
213                                                 return NULL;
214                                         textline->remainder = ptr;
215                                         textline->remainder_size = 1;
216                                         end = ptr + 1;
217                                 } else
218                                         ptr = end = NULL;
219                         }
220                         if (ptr != NULL &&
221                             ((last == '\n' && *ptr == '\r') ||
222                              (last == '\r' && *ptr == '\n')))
223                                 ptr++;
224                         break;
225                 } else if (gsf_input_remaining (textline->source) <= 0) {
226                         ptr = end = NULL;
227                         break;
228                 } else
229                         textline->remainder = NULL;
230
231         }
232
233         textline->remainder = ptr;
234         textline->remainder_size = end - ptr;
235
236         textline->buf [count] = '\0';
237         return textline->buf;
238 }
239
240 /**
241  * gsf_input_textline_utf8_gets :
242  * @textline : #GsfInputTextline
243  *
244  * A utility routine to read things line by line from the underlying source.
245  * Trailing newlines and carriage returns are stripped, and the resultant buffer
246  * can be edited.
247  *
248  * Returns: the string read, or %NULL on eof.
249  **/
250 guint8 *
251 gsf_input_textline_utf8_gets (GsfInputTextline *textline)
252 {
253         guint8 const *ptr ,*end;
254         gsf_off_t remain;
255         unsigned len, count = 0;
256
257         g_return_val_if_fail (textline != NULL, NULL);
258
259         while (1) {
260                 if (textline->remainder == NULL ||
261                     textline->remainder_size == 0) {
262                         remain = gsf_input_remaining (textline->source);
263                         len = MIN (remain, textline->max_line_size);
264
265                         textline->remainder = gsf_input_read (textline->source, len, NULL);
266                         if (textline->remainder == NULL)
267                                 return NULL;
268                         textline->remainder_size = len;
269                 }
270
271                 ptr = textline->remainder;
272                 end = ptr + textline->remainder_size;
273                 for (; ptr < end ; ptr = (guint8 *) g_utf8_next_char (ptr))
274                         if (*ptr == '\n' || *ptr == '\r')
275                                 break;
276
277                 /* copy the remains into the buffer, grow it if necessary */
278                 len = ptr - textline->remainder;
279                 if (count + len >= textline->buf_size) {
280                         textline->buf_size += len;
281                         textline->buf = g_renew (guint8, textline->buf,
282                                                  textline->buf_size + 1);
283                 }
284
285                 g_return_val_if_fail (textline->buf != NULL, NULL);
286
287                 memcpy (textline->buf + count, textline->remainder, len);
288                 count += len;
289
290                 if (ptr < end) {
291                         unsigned char last = ptr [0];
292
293                         /* eat the trailing new line */
294                         ptr++;
295                         if (ptr >= end) {
296                                 /* be extra careful, the newline is at the bound */
297                                 if (gsf_input_remaining (textline->source) > 0) {
298                                         ptr = gsf_input_read (textline->source, 1, NULL);
299                                         if (ptr == NULL)
300                                                 return NULL;
301                                         textline->remainder = ptr;
302                                         textline->remainder_size = 1;
303                                         end = ptr + 1;
304                                 } else
305                                         ptr = end = NULL;
306                         }
307                         if (ptr != NULL &&
308                             ((last == '\n' && *ptr == '\r') ||
309                              (last == '\r' && *ptr == '\n')))
310                                 ptr++;
311                         break;
312                 } else if (gsf_input_remaining (textline->source) <= 0) {
313                         ptr = end = NULL;
314                         break;
315                 } else
316                         textline->remainder = NULL;
317
318         }
319
320         textline->remainder = ptr;
321         textline->remainder_size = end - ptr;
322
323         textline->buf [count] = '\0';
324         return textline->buf;
325 }