1 /*===========================================================================
2  *  Filename : scmport-str.c
3  *  About    : A ScmBytePort implementation for string
4  *
5  *  Copyright (C) 2005-2006 YAMAMOTO Kengo <yamaken AT bp.iij4u.or.jp>
6  *  Copyright (c) 2007-2008 SigScheme Project <uim-en AT googlegroups.com>
7  *
8  *  All rights reserved.
9  *
10  *  Redistribution and use in source and binary forms, with or without
11  *  modification, are permitted provided that the following conditions
12  *  are met:
13  *
14  *  1. Redistributions of source code must retain the above copyright
15  *     notice, this list of conditions and the following disclaimer.
16  *  2. Redistributions in binary form must reproduce the above copyright
17  *     notice, this list of conditions and the following disclaimer in the
18  *     documentation and/or other materials provided with the distribution.
19  *  3. Neither the name of authors nor the names of its contributors
20  *     may be used to endorse or promote products derived from this software
21  *     without specific prior written permission.
22  *
23  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
24  *  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25  *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
27  *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28  *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29  *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30  *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32  *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33  *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 ===========================================================================*/
35 
36 /*
37  * - This file is intended to be portable. Don't depend on SigScheme.
38  * - To isolate and hide implementation-dependent things, don't merge this file
39  *   into another
40  */
41 
42 #include <config.h>
43 
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 
48 #include "scmint.h"
49 #include "scmport-config.h"
50 #include "scmport.h"
51 #include "scmport-str.h"
52 
53 /*=======================================
54   File Local Macro Definitions
55 =======================================*/
56 
57 /*=======================================
58   File Local Type Definitions
59 =======================================*/
60 typedef struct ScmInputStrPort_  ScmInputStrPort;
61 typedef struct ScmOutputStrPort_ ScmOutputStrPort;
62 
63 struct ScmInputStrPort_ {  /* inherits ScmBytePort */
64     const ScmBytePortVTbl *vptr;
65 
66     scm_byte_t *str;
67     const scm_byte_t *cur;
68     scm_bool has_str_ownership;
69     void *opaque;  /* client-specific opaque information */
70     ScmInputStrPort_finalizer finalize;
71 };
72 
73 struct ScmOutputStrPort_ {  /* inherits ScmBytePort */
74     const ScmBytePortVTbl *vptr;
75 
76     scm_byte_t *str;
77     size_t cur;       /* offset to terminal '\0' */
78     size_t buf_size;
79     void *opaque;     /* client-specific opaque information */
80     ScmOutputStrPort_finalizer finalize;
81 };
82 
83 /*=======================================
84   File Local Function Declarations
85 =======================================*/
86 /* input port */
87 static ScmBytePort *istrport_new(scm_byte_t *str, scm_bool ownership,
88                                  ScmInputStrPort_finalizer finalize);
89 static void istrport_finalize(char **str, scm_bool ownership, void **opaque);
90 
91 static ScmBytePort *istrport_dyn_cast(ScmBytePort *bport,
92                                       const ScmBytePortVTbl *dest_vptr);
93 static void istrport_close(ScmInputStrPort *port);
94 static char *istrport_inspect(ScmInputStrPort *port);
95 static scm_ichar_t istrport_get_byte(ScmInputStrPort *port);
96 static scm_ichar_t istrport_peek_byte(ScmInputStrPort *port);
97 static scm_bool istrport_byte_readyp(ScmInputStrPort *port);
98 static void istrport_puts(ScmInputStrPort *port, const char *str);
99 static void istrport_write(ScmInputStrPort *port,
100                            size_t nbytes, const char *buf);
101 static void istrport_flush(ScmInputStrPort *port);
102 
103 /* output port */
104 static void ostrport_finalize(char **str, size_t buf_size, void **opaque);
105 
106 static ScmBytePort *ostrport_dyn_cast(ScmBytePort *bport,
107                                       const ScmBytePortVTbl *dest_vptr);
108 static void ostrport_close(ScmOutputStrPort *port);
109 static char *ostrport_inspect(ScmOutputStrPort *port);
110 static scm_ichar_t ostrport_get_byte(ScmOutputStrPort *port);
111 static scm_ichar_t ostrport_peek_byte(ScmOutputStrPort *port);
112 static scm_bool ostrport_byte_readyp(ScmOutputStrPort *port);
113 static void ostrport_puts(ScmOutputStrPort *port, const char *str);
114 static void ostrport_write(ScmOutputStrPort *port,
115                            size_t nbytes, const char *buf);
116 static void ostrport_flush(ScmOutputStrPort *port);
117 
118 static void ostrport_append(ScmOutputStrPort *port,
119                             size_t len, const scm_byte_t *str);
120 
121 /*=======================================
122   Variable Definitions
123 =======================================*/
124 static const ScmBytePortVTbl ScmInputStrPort_vtbl = {
125     (ScmBytePortMethod_dyn_cast)   &istrport_dyn_cast,
126     (ScmBytePortMethod_close)      &istrport_close,
127     (ScmBytePortMethod_inspect)    &istrport_inspect,
128     (ScmBytePortMethod_get_byte)   &istrport_get_byte,
129     (ScmBytePortMethod_peek_byte)  &istrport_peek_byte,
130     (ScmBytePortMethod_byte_readyp)&istrport_byte_readyp,
131     (ScmBytePortMethod_puts)       &istrport_puts,
132     (ScmBytePortMethod_write)      &istrport_write,
133     (ScmBytePortMethod_flush)      &istrport_flush
134 };
135 SCM_EXPORT const ScmBytePortVTbl *const ScmInputStrPort_vptr
136     = &ScmInputStrPort_vtbl;
137 
138 static const ScmBytePortVTbl ScmOutputStrPort_vtbl = {
139     (ScmBytePortMethod_dyn_cast)   &ostrport_dyn_cast,
140     (ScmBytePortMethod_close)      &ostrport_close,
141     (ScmBytePortMethod_inspect)    &ostrport_inspect,
142     (ScmBytePortMethod_get_byte)   &ostrport_get_byte,
143     (ScmBytePortMethod_peek_byte)  &ostrport_peek_byte,
144     (ScmBytePortMethod_byte_readyp)&ostrport_byte_readyp,
145     (ScmBytePortMethod_puts)       &ostrport_puts,
146     (ScmBytePortMethod_write)      &ostrport_write,
147     (ScmBytePortMethod_flush)      &ostrport_flush
148 };
149 SCM_EXPORT const ScmBytePortVTbl *const ScmOutputStrPort_vptr
150     = &ScmOutputStrPort_vtbl;
151 
152 /*=======================================
153   Function Definitions
154 =======================================*/
155 
156 /*
157  * Client code must call this first even if current implementation does not
158  * contain actual code.
159  */
160 SCM_EXPORT void
scm_strport_init(void)161 scm_strport_init(void)
162 {
163 }
164 
165 static ScmBytePort *
istrport_new(scm_byte_t * str,scm_bool ownership,ScmInputStrPort_finalizer finalize)166 istrport_new(scm_byte_t *str, scm_bool ownership,
167              ScmInputStrPort_finalizer finalize)
168 {
169     ScmInputStrPort *port;
170 
171     SCM_PORT_ASSERT(str);
172 
173     port = SCM_PORT_MALLOC(sizeof(ScmInputStrPort));
174 
175     port->vptr = ScmInputStrPort_vptr;
176     port->cur = port->str = str;
177     port->has_str_ownership = ownership;
178     port->opaque = NULL;
179     port->finalize = (finalize) ? finalize : &istrport_finalize;
180 
181     return (ScmBytePort *)port;
182 }
183 
184 SCM_EXPORT ScmBytePort *
ScmInputStrPort_new(char * str,ScmInputStrPort_finalizer finalize)185 ScmInputStrPort_new(char *str, ScmInputStrPort_finalizer finalize)
186 {
187     return istrport_new((scm_byte_t *)str, scm_true, finalize);
188 }
189 
190 SCM_EXPORT ScmBytePort *
ScmInputStrPort_new_copying(const char * str,ScmInputStrPort_finalizer finalize)191 ScmInputStrPort_new_copying(const char *str,
192                             ScmInputStrPort_finalizer finalize)
193 {
194     return istrport_new((scm_byte_t *)SCM_PORT_STRDUP(str),
195                         scm_true, finalize);
196 }
197 
198 SCM_EXPORT ScmBytePort *
ScmInputStrPort_new_const(const char * str,ScmInputStrPort_finalizer finalize)199 ScmInputStrPort_new_const(const char *str, ScmInputStrPort_finalizer finalize)
200 {
201     /* str is actually treated as const */
202     return istrport_new((scm_byte_t *)str, scm_false, finalize);
203 }
204 
205 SCM_EXPORT void **
ScmInputStrPort_ref_opaque(ScmBytePort * bport)206 ScmInputStrPort_ref_opaque(ScmBytePort *bport)
207 {
208     ScmInputStrPort *port;
209 
210     port = SCM_BYTEPORT_DYNAMIC_CAST(ScmInputStrPort, bport);
211 
212     return &port->opaque;
213 }
214 
215 static ScmBytePort *
istrport_dyn_cast(ScmBytePort * bport,const ScmBytePortVTbl * dst_vptr)216 istrport_dyn_cast(ScmBytePort *bport, const ScmBytePortVTbl *dst_vptr)
217 {
218     return (dst_vptr == ScmInputStrPort_vptr) ? bport : NULL;
219 }
220 
221 /* default finalizer */
222 static void
istrport_finalize(char ** str,scm_bool ownership,void ** opaque)223 istrport_finalize(char **str, scm_bool ownership, void **opaque)
224 {
225     if (ownership)
226         free(*str);
227 }
228 
229 static void
istrport_close(ScmInputStrPort * port)230 istrport_close(ScmInputStrPort *port)
231 {
232     SCM_PORT_ASSERT(port->finalize);
233 
234     /* To suppress the "dereferencing type-punned pointer will break
235      * strict-aliasing rules" warning on gcc, apply a reinterpret_cast
236      * here. This operation is safe on the case.   -- YamaKen 2007-01-06 */
237     (*port->finalize)((char **)(uintptr_t)&port->str,
238                       port->has_str_ownership, &port->opaque);
239     free(port);
240 }
241 
242 static char *
istrport_inspect(ScmInputStrPort * port)243 istrport_inspect(ScmInputStrPort *port)
244 {
245     return SCM_PORT_STRDUP("string");
246 }
247 
248 static scm_ichar_t
istrport_get_byte(ScmInputStrPort * port)249 istrport_get_byte(ScmInputStrPort *port)
250 {
251     return (*port->cur) ? *port->cur++ : SCM_ICHAR_EOF;
252 }
253 
254 static scm_ichar_t
istrport_peek_byte(ScmInputStrPort * port)255 istrport_peek_byte(ScmInputStrPort *port)
256 {
257     return (*port->cur) ? *port->cur : SCM_ICHAR_EOF;
258 }
259 
260 static scm_bool
istrport_byte_readyp(ScmInputStrPort * port)261 istrport_byte_readyp(ScmInputStrPort *port)
262 {
263     return scm_true;
264 }
265 
266 static void
istrport_puts(ScmInputStrPort * port,const char * str)267 istrport_puts(ScmInputStrPort *port, const char *str)
268 {
269     SCM_PORT_ERROR_INVALID_OPERATION(BYTE, port, ScmInputStrPort);
270     /* NOTREACHED */
271 }
272 
273 static void
istrport_write(ScmInputStrPort * port,size_t nbytes,const char * buf)274 istrport_write(ScmInputStrPort *port, size_t nbytes, const char *buf)
275 {
276     SCM_PORT_ERROR_INVALID_OPERATION(BYTE, port, ScmInputStrPort);
277     /* NOTREACHED */
278 }
279 
280 static void
istrport_flush(ScmInputStrPort * port)281 istrport_flush(ScmInputStrPort *port)
282 {
283     SCM_PORT_ERROR_INVALID_OPERATION(BYTE, port, ScmInputStrPort);
284     /* NOTREACHED */
285 }
286 
287 
288 SCM_EXPORT ScmBytePort *
ScmOutputStrPort_new(ScmOutputStrPort_finalizer finalize)289 ScmOutputStrPort_new(ScmOutputStrPort_finalizer finalize)
290 {
291     ScmOutputStrPort *port;
292 
293     port = SCM_PORT_MALLOC(sizeof(ScmOutputStrPort));
294 
295     port->vptr = ScmOutputStrPort_vptr;
296     port->str = NULL;
297     port->cur = 0;
298     port->buf_size = 0;
299     port->opaque = NULL;
300     port->finalize = (finalize) ? finalize : &ostrport_finalize;
301 
302     return (ScmBytePort *)port;
303 }
304 
305 SCM_EXPORT const char *
ScmOutputStrPort_str(ScmBytePort * bport)306 ScmOutputStrPort_str(ScmBytePort *bport)
307 {
308     ScmOutputStrPort *port;
309 
310     port = SCM_BYTEPORT_DYNAMIC_CAST(ScmOutputStrPort, bport);
311 
312     return (port->str) ? (const char *)port->str : "";
313 }
314 
315 SCM_EXPORT size_t
ScmOutputStrPort_c_strlen(ScmBytePort * bport)316 ScmOutputStrPort_c_strlen(ScmBytePort *bport)
317 {
318     ScmOutputStrPort *port;
319 
320     port = SCM_BYTEPORT_DYNAMIC_CAST(ScmOutputStrPort, bport);
321 
322     return (port->buf_size) ? port->buf_size - sizeof("") : 0;
323 }
324 
325 SCM_EXPORT void **
ScmOutputStrPort_ref_opaque(ScmBytePort * bport)326 ScmOutputStrPort_ref_opaque(ScmBytePort *bport)
327 {
328     ScmOutputStrPort *port;
329 
330     port = SCM_BYTEPORT_DYNAMIC_CAST(ScmOutputStrPort, bport);
331 
332     return &port->opaque;
333 }
334 
335 static ScmBytePort *
ostrport_dyn_cast(ScmBytePort * bport,const ScmBytePortVTbl * dst_vptr)336 ostrport_dyn_cast(ScmBytePort *bport, const ScmBytePortVTbl *dst_vptr)
337 {
338     return (dst_vptr == ScmOutputStrPort_vptr) ? bport : NULL;
339 }
340 
341 static void
ostrport_finalize(char ** str,size_t buf_size,void ** opaque)342 ostrport_finalize(char **str, size_t buf_size, void **opaque)
343 {
344     free(*str);
345 }
346 
347 static void
ostrport_close(ScmOutputStrPort * port)348 ostrport_close(ScmOutputStrPort *port)
349 {
350     SCM_PORT_ASSERT(port->finalize);
351 
352     /* To suppress the "dereferencing type-punned pointer will break
353      * strict-aliasing rules" warning on gcc, apply a reinterpret_cast
354      * here. This operation is safe on the case.   -- YamaKen 2007-01-06 */
355     (*port->finalize)((char **)(uintptr_t)&port->str,
356                       port->buf_size, &port->opaque);
357     free(port);
358 }
359 
360 static char *
ostrport_inspect(ScmOutputStrPort * port)361 ostrport_inspect(ScmOutputStrPort *port)
362 {
363     return SCM_PORT_STRDUP("string");
364 }
365 
366 static scm_ichar_t
ostrport_get_byte(ScmOutputStrPort * port)367 ostrport_get_byte(ScmOutputStrPort *port)
368 {
369     SCM_PORT_ERROR_INVALID_OPERATION(BYTE, port, ScmOutputStrPort);
370     /* NOTREACHED */
371 }
372 
373 static scm_ichar_t
ostrport_peek_byte(ScmOutputStrPort * port)374 ostrport_peek_byte(ScmOutputStrPort *port)
375 {
376     SCM_PORT_ERROR_INVALID_OPERATION(BYTE, port, ScmOutputStrPort);
377     /* NOTREACHED */
378 }
379 
380 static scm_bool
ostrport_byte_readyp(ScmOutputStrPort * port)381 ostrport_byte_readyp(ScmOutputStrPort *port)
382 {
383     SCM_PORT_ERROR_INVALID_OPERATION(BYTE, port, ScmOutputStrPort);
384     /* NOTREACHED */
385 }
386 
387 static void
ostrport_puts(ScmOutputStrPort * port,const char * str)388 ostrport_puts(ScmOutputStrPort *port, const char *str)
389 {
390     ostrport_append(port, strlen(str), (const scm_byte_t *)str);
391 }
392 
393 static void
ostrport_write(ScmOutputStrPort * port,size_t nbytes,const char * buf)394 ostrport_write(ScmOutputStrPort *port, size_t nbytes, const char *buf)
395 {
396     ostrport_append(port, nbytes, (const scm_byte_t *)buf);
397 }
398 
399 static void
ostrport_flush(ScmOutputStrPort * port)400 ostrport_flush(ScmOutputStrPort *port)
401 {
402 }
403 
404 static void
ostrport_append(ScmOutputStrPort * port,size_t len,const scm_byte_t * str)405 ostrport_append(ScmOutputStrPort *port, size_t len, const scm_byte_t *str)
406 {
407     SCM_PORT_ASSERT(str);
408 
409     /* extend the buffer */
410     if (port->buf_size - port->cur < len + sizeof("")) {
411         if (!port->buf_size)
412             port->buf_size = sizeof("");
413 
414         port->buf_size += len;
415         port->str = SCM_PORT_REALLOC(port->str, port->buf_size);
416     }
417 
418     memcpy(port->str + port->cur, str, len);
419     port->cur += len;
420     port->str[port->cur] = '\0';
421 }
422