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