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.h>
25
26 #include <string.h>
27
28 static GObjectClass *parent_class;
29
30 struct _GsfInputTextline {
31 GsfInput input;
32
33 GsfInput *source;
34 guint8 const *remainder;
35 unsigned remainder_size;
36 unsigned max_line_size;
37
38 unsigned char *buf;
39 unsigned buf_size;
40
41 /* int current_line; */
42 };
43
44 typedef struct {
45 GsfInputClass input_class;
46 } GsfInputTextlineClass;
47
48 /**
49 * gsf_input_textline_new:
50 * @source: in some combination of ascii and utf8
51 *
52 * <note>This adds a reference to @source.</note>
53 *
54 * Returns: (nullable): a new file
55 **/
56 GsfInput *
gsf_input_textline_new(GsfInput * source)57 gsf_input_textline_new (GsfInput *source)
58 {
59 GsfInputTextline *input;
60
61 g_return_val_if_fail (source != NULL, NULL);
62
63 input = g_object_new (GSF_INPUT_TEXTLINE_TYPE, NULL);
64 input->source = g_object_ref (source);
65 input->buf = NULL;
66 input->buf_size = 0;
67 gsf_input_set_size (GSF_INPUT (input), gsf_input_size (source));
68 gsf_input_set_name (GSF_INPUT (input), gsf_input_name (source));
69
70 return GSF_INPUT (input);
71 }
72
73 static void
gsf_input_textline_finalize(GObject * obj)74 gsf_input_textline_finalize (GObject *obj)
75 {
76 GsfInputTextline *input = (GsfInputTextline *)obj;
77
78 if (input->source != NULL) {
79 g_object_unref (input->source);
80 input->source = NULL;
81 }
82 g_free (input->buf);
83 input->buf = NULL;
84 input->buf_size = 0;
85
86 parent_class->finalize (obj);
87 }
88
89 static GsfInput *
gsf_input_textline_dup(GsfInput * src_input,G_GNUC_UNUSED GError ** err)90 gsf_input_textline_dup (GsfInput *src_input, G_GNUC_UNUSED GError **err)
91 {
92 GsfInputTextline const *src = (GsfInputTextline *)src_input;
93 GsfInputTextline *dst = g_object_new (GSF_INPUT_TEXTLINE_TYPE, NULL);
94
95 dst->source = g_object_ref (src->source);
96 gsf_input_set_size (GSF_INPUT (dst), gsf_input_size (src_input));
97 gsf_input_set_name (GSF_INPUT (dst), gsf_input_name (src_input));
98
99 return GSF_INPUT (dst);
100 }
101
102 static guint8 const *
gsf_input_textline_read(GsfInput * input,size_t num_bytes,guint8 * buffer)103 gsf_input_textline_read (GsfInput *input, size_t num_bytes, guint8 *buffer)
104 {
105 GsfInputTextline *textline = GSF_INPUT_TEXTLINE (input);
106 const guint8 *res;
107
108 textline->remainder = NULL;
109 res = gsf_input_read (textline->source, num_bytes, buffer);
110 input->cur_offset = textline->source->cur_offset;
111
112 return res;
113 }
114
115 static gboolean
gsf_input_textline_seek(GsfInput * input,gsf_off_t offset,GSeekType whence)116 gsf_input_textline_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
117 {
118 GsfInputTextline *textline = GSF_INPUT_TEXTLINE (input);
119 gboolean res;
120
121 textline->remainder = NULL;
122 res = gsf_input_seek (textline->source, offset, whence);
123 input->cur_offset = textline->source->cur_offset;
124
125 return res;
126 }
127
128 static void
gsf_input_textline_init(GObject * obj)129 gsf_input_textline_init (GObject *obj)
130 {
131 GsfInputTextline *textline = GSF_INPUT_TEXTLINE (obj);
132
133 textline->source = NULL;
134 textline->remainder = NULL;
135 textline->remainder_size = 0;
136 textline->max_line_size = 512; /* an initial guess */
137 textline->buf = NULL;
138 textline->buf_size = 0;
139 }
140
141 static void
gsf_input_textline_class_init(GObjectClass * gobject_class)142 gsf_input_textline_class_init (GObjectClass *gobject_class)
143 {
144 GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
145
146 gobject_class->finalize = gsf_input_textline_finalize;
147 input_class->Dup = gsf_input_textline_dup;
148 input_class->Read = gsf_input_textline_read;
149 input_class->Seek = gsf_input_textline_seek;
150
151 parent_class = g_type_class_peek_parent (gobject_class);
152 }
153
GSF_CLASS(GsfInputTextline,gsf_input_textline,gsf_input_textline_class_init,gsf_input_textline_init,GSF_INPUT_TYPE)154 GSF_CLASS (GsfInputTextline, gsf_input_textline,
155 gsf_input_textline_class_init, gsf_input_textline_init,
156 GSF_INPUT_TYPE)
157
158 /**
159 * gsf_input_textline_ascii_gets:
160 * @textline: #GsfInputTextline
161 *
162 * A utility routine to read things line by line from the underlying source.
163 * Trailing newlines and carriage returns are stripped, and the resultant buffer
164 * can be edited.
165 *
166 * Returns: (array) (transfer none) (nullable): the string read, or %NULL on eof.
167 **/
168 unsigned char *
169 gsf_input_textline_ascii_gets (GsfInputTextline *textline)
170 {
171 return gsf_input_textline_utf8_gets (textline);
172 }
173
174 /**
175 * gsf_input_textline_utf8_gets:
176 * @textline: #GsfInputTextline
177 *
178 * A utility routine to read things line by line from the underlying source.
179 * Trailing newlines and carriage returns are stripped, and the resultant buffer
180 * can be edited.
181 *
182 * Returns: (array) (transfer none) (nullable): the string read, or %NULL on eof.
183 **/
184 guint8 *
gsf_input_textline_utf8_gets(GsfInputTextline * textline)185 gsf_input_textline_utf8_gets (GsfInputTextline *textline)
186 {
187 guint8 const *ptr ,*end;
188 unsigned len, count = 0;
189
190 g_return_val_if_fail (textline != NULL, NULL);
191
192 while (1) {
193 if (textline->remainder == NULL ||
194 textline->remainder_size == 0) {
195 gsf_off_t remain = gsf_input_remaining (textline->source);
196 len = MIN (remain, textline->max_line_size);
197
198 textline->remainder = gsf_input_read (textline->source, len, NULL);
199 if (textline->remainder == NULL)
200 return NULL;
201 textline->remainder_size = len;
202 }
203
204 ptr = textline->remainder;
205 end = ptr + textline->remainder_size;
206 for (; ptr < end ; ptr++)
207 if (*ptr == '\n' || *ptr == '\r')
208 break;
209
210 /* copy the remains into the buffer, grow it if necessary */
211 len = ptr - textline->remainder;
212 if (count + len >= textline->buf_size) {
213 textline->buf_size += len;
214 textline->buf = g_renew (guint8, textline->buf,
215 textline->buf_size + 1);
216 }
217
218 g_return_val_if_fail (textline->buf != NULL, NULL);
219
220 memcpy (textline->buf + count, textline->remainder, len);
221 count += len;
222
223 if (ptr < end) {
224 unsigned char last = ptr[0];
225
226 /* eat the trailing eol marker: \n, \r\n, or \r. */
227 ptr++;
228 if (ptr >= end && last == '\r') {
229 /* be extra careful, the CR is at the bound */
230 if (gsf_input_remaining (textline->source) > 0) {
231 ptr = gsf_input_read (textline->source, 1, NULL);
232 if (ptr == NULL)
233 return NULL;
234 textline->remainder = ptr;
235 textline->remainder_size = 1;
236 end = ptr + 1;
237 } else
238 ptr = end = NULL;
239 }
240 if (ptr != NULL && last == '\r' && *ptr == '\n')
241 ptr++;
242 break;
243 } else if (gsf_input_remaining (textline->source) <= 0) {
244 ptr = end = NULL;
245 break;
246 } else
247 textline->remainder = NULL;
248 }
249
250 textline->remainder = ptr;
251 textline->remainder_size = end - ptr;
252
253 GSF_INPUT(textline)->cur_offset = textline->source->cur_offset -
254 (textline->remainder ? textline->remainder_size : 0);
255
256 textline->buf[count] = '\0';
257 return textline->buf;
258 }
259