1/***********************************************************************
2 *                                                                     *
3 * $Id: hpgsostream.c 368 2006-12-31 15:18:08Z softadm $
4 *                                                                     *
5 * hpgs - HPGl Script, a hpgl/2 interpreter, which uses a Postscript   *
6 *        API for rendering a scene and thus renders to a variety of   *
7 *        devices and fileformats.                                     *
8 *                                                                     *
9 * (C) 2004-2006 ev-i Informationstechnologie GmbH  http://www.ev-i.at *
10 *                                                                     *
11 * Author: Wolfgang Glas                                               *
12 *                                                                     *
13 *  hpgs is free software; you can redistribute it and/or              *
14 * modify it under the terms of the GNU Lesser General Public          *
15 * License as published by the Free Software Foundation; either        *
16 * version 2.1 of the License, or (at your option) any later version.  *
17 *                                                                     *
18 * hpgs is distributed in the hope that it will be useful,             *
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of      *
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   *
21 * Lesser General Public License for more details.                     *
22 *                                                                     *
23 * You should have received a copy of the GNU Lesser General Public    *
24 * License along with this library; if not, write to the               *
25 * Free Software  Foundation, Inc., 59 Temple Place, Suite 330,        *
26 * Boston, MA  02111-1307  USA                                         *
27 *                                                                     *
28 ***********************************************************************
29 *                                                                     *
30 * The implementation of basic output streams.                         *
31 *                                                                     *
32 ***********************************************************************/
33
34#include<hpgs.h>
35#include<string.h>
36#include<stdarg.h>
37
38/*! The counterpart of ANSI fprintf for \c hpgs_ostream. */
39int hpgs_ostream_printf (hpgs_ostream *_this, const char *msg, ...)
40{
41  int ret;
42  va_list ap;
43  va_start(ap,msg);
44  ret=_this->vtable->vprintf_func(_this->stream,msg,ap);
45  va_end(ap);
46  return ret;
47}
48
49/*! The counterpart of ANSI fvprintf for \c hpgs_ostream. */
50int hpgs_ostream_vprintf (hpgs_ostream *_this, const char *msg, va_list ap)
51{
52  int ret;
53  va_list aq;
54  va_copy(aq, ap);
55  ret = _this->vtable->vprintf_func(_this->stream,msg,aq);
56  va_end(aq);
57  return ret;
58}
59
60
61static int file_seek (FILE *file, size_t pos)
62{
63  return fseek(file,pos,SEEK_SET);
64}
65
66static int file_tell (FILE *file, int layer, size_t *pos)
67{
68  if (layer) return -1;
69
70  long pp=ftell(file);
71
72  if (pp<0) return -1;
73
74  *pos = pp;
75  return 0;
76}
77
78static hpgs_ostream_vtable stdfile_vtable =
79  {
80    (hpgs_ostream_putc_func_t)    putc,
81    (hpgs_ostream_write_func_t)   fwrite,
82    (hpgs_ostream_vprintf_func_t) vfprintf,
83    (hpgs_ostream_flush_func_t)   fflush,
84    (hpgs_ostream_close_func_t)   fflush,
85    (hpgs_ostream_iserror_func_t) ferror,
86    (hpgs_ostream_getbuf_func_t)  0,
87    (hpgs_ostream_tell_func_t)    file_tell,
88    (hpgs_ostream_seek_func_t)    0
89  };
90
91static hpgs_ostream_vtable file_vtable =
92  {
93    (hpgs_ostream_putc_func_t)    putc,
94    (hpgs_ostream_write_func_t)   fwrite,
95    (hpgs_ostream_vprintf_func_t) vfprintf,
96    (hpgs_ostream_flush_func_t)   fflush,
97    (hpgs_ostream_close_func_t)   fclose,
98    (hpgs_ostream_iserror_func_t) ferror,
99    (hpgs_ostream_getbuf_func_t)  0,
100    (hpgs_ostream_tell_func_t)    file_tell,
101    (hpgs_ostream_seek_func_t)    file_seek
102  };
103
104/*! Returns a new \c hpgs_ostream created on the heap,
105    which operates on a file, which is opened by this call
106    in write-only mode.
107
108    Returns a null pointer, when an I/O error occurrs.
109    In this case, details about the the error can be retrieved
110    using \c errno.
111  */
112hpgs_ostream *hpgs_new_file_ostream (const char *fn)
113{
114  FILE *out = fn ? fopen (fn,"wb") : stdout;
115  hpgs_ostream *ret = 0;
116
117  if (!out) return 0;
118
119  ret = (hpgs_ostream *)malloc(sizeof(hpgs_ostream));
120
121  if (!ret)
122    {
123      fclose(out);
124      return 0;
125    }
126
127  ret->stream = out;
128
129  if (fn)
130    ret->vtable = &file_vtable;
131  else
132    ret->vtable = &stdfile_vtable;
133
134  return ret;
135}
136
137typedef struct hpgs_mem_ostream_stream_st hpgs_mem_ostream_stream;
138
139struct hpgs_mem_ostream_stream_st
140{
141  unsigned char *data;
142  unsigned char *pptr;
143  unsigned char *eptr;
144  int errflg;
145};
146
147static int mem_grow (hpgs_mem_ostream_stream *stream, size_t hint)
148{
149  if (stream->errflg)
150    return EOF;
151
152  size_t pos = stream->pptr-stream->data;
153  size_t sz  = stream->eptr-stream->data;
154  size_t sz2 = (hint > sz) ? sz+hint : 2*sz;
155
156  // check for overflow.
157  if (sz2 < sz)
158    {
159      stream->errflg = 1;
160      return EOF;
161    }
162
163  unsigned char *ndata=(unsigned char *)realloc(stream->data,sz2);
164
165  if (ndata)
166    {
167      stream->data=ndata;
168      stream->pptr=stream->data+pos;
169      stream->eptr=stream->data+sz2;
170      return 0;
171    }
172  else
173    {
174      stream->errflg = 1;
175      return EOF;
176    }
177}
178
179static int mem_putc (int c, hpgs_mem_ostream_stream *stream)
180{
181  if (stream->pptr >= stream->eptr)
182    mem_grow(stream,0);
183
184  if (stream->errflg)
185    return EOF;
186
187  *stream->pptr = c;
188  ++stream->pptr;
189
190  return c & 0xff;
191}
192
193static size_t mem_write (void *ptr, size_t size, size_t nmemb, hpgs_mem_ostream_stream *stream)
194{
195  if (stream->pptr + size*nmemb > stream->eptr)
196    mem_grow(stream,size*nmemb);
197
198  if (stream->errflg)
199    return 0;
200
201  memcpy(stream->pptr,ptr,size*nmemb);
202  stream->pptr += size*nmemb;
203
204  return nmemb;
205}
206
207static int mem_vprintf (hpgs_mem_ostream_stream *stream, const char *fmt, va_list ap)
208{
209  int n;
210  size_t size;
211
212  while (1)
213    {
214      if (stream->errflg)
215        return EOF;
216
217      size = stream->eptr - stream->pptr;
218      /* Try to print in the allocated space. */
219      va_list aq;
220      va_copy(aq, ap);
221#ifdef WIN32
222      n = _vsnprintf (stream->pptr, size, fmt, aq);
223#else
224      n = vsnprintf ((char*)stream->pptr, size, fmt, aq);
225#endif
226      va_end(aq);
227      /* If that worked, return the string. */
228      if (n > -1 && n < size)
229	{
230          stream->pptr += n;
231          return n;
232        }
233
234      /* Else try again with more space. */
235      mem_grow(stream,0);
236    }
237
238  return 0;
239}
240
241static int mem_close (hpgs_mem_ostream_stream *stream)
242{
243  if (stream->data) free(stream->data);
244  free(stream);
245  return 0;
246}
247
248static int mem_iserror (hpgs_mem_ostream_stream *stream)
249{
250  return stream->errflg;
251}
252
253static hpgs_istream *mem_getbuf (hpgs_mem_ostream_stream *stream)
254{
255  if (stream->errflg || !stream->data)
256    return 0;
257
258  return hpgs_new_mem_istream (stream->data,stream->pptr-stream->data,HPGS_FALSE);
259}
260
261static int mem_tell (hpgs_mem_ostream_stream *stream, int layer, size_t *pos)
262{
263  if (layer || stream->errflg) return -1;
264
265  *pos = stream->pptr-stream->data;
266  return 0;
267}
268
269static int mem_seek (hpgs_mem_ostream_stream *stream, size_t pos)
270{
271  if (stream->errflg) return -1;
272
273  if (stream->data + pos > stream->pptr)
274    {
275      stream->errflg = 1;
276      return -1;
277    }
278
279  stream->pptr = stream->data + pos;
280  return 0;
281}
282
283static hpgs_ostream_vtable mem_vtable =
284  {
285    (hpgs_ostream_putc_func_t)    mem_putc,
286    (hpgs_ostream_write_func_t)   mem_write,
287    (hpgs_ostream_vprintf_func_t) mem_vprintf,
288    (hpgs_ostream_flush_func_t)   0,
289    (hpgs_ostream_close_func_t)   mem_close,
290    (hpgs_ostream_iserror_func_t) mem_iserror,
291    (hpgs_ostream_getbuf_func_t)  mem_getbuf,
292    (hpgs_ostream_tell_func_t)    mem_tell,
293    (hpgs_ostream_seek_func_t)    mem_seek
294  };
295
296/*! Returns a new \c hpgs_ostream created on the heap,
297    which operates on a malloced chunk of memory with
298    the preallocated given size.
299
300    The memory buffer is realloced when the data grows over the
301    given preallocated size.
302
303    Returns a null pointer, when the system is out of memory.
304  */
305hpgs_ostream *hpgs_new_mem_ostream (size_t data_reserve)
306{
307  hpgs_mem_ostream_stream *stream;
308  hpgs_ostream *ret = (hpgs_ostream *)malloc(sizeof(hpgs_ostream));
309
310  if (!ret)
311    return 0;
312
313  stream =
314    (hpgs_mem_ostream_stream *)malloc(sizeof(hpgs_mem_ostream_stream));
315
316  if (!stream)
317    {
318      free (ret);
319      return 0;
320    }
321
322  stream->data = (unsigned char *)malloc(data_reserve);
323  stream->pptr = stream->data;
324  stream->eptr =  stream->data ? stream->data+data_reserve : 0;
325  stream->errflg = stream->data ? 0 : 1;
326
327  ret->stream = stream;
328  ret->vtable = &mem_vtable;
329
330  return ret;
331}
332
333int hpgs_copy_streams (hpgs_ostream *out, hpgs_istream *in)
334{
335  unsigned char buf[16384];
336  size_t sz;
337
338  while ((sz = hpgs_istream_read(buf,1,sizeof(buf),in)) > 0)
339    if (hpgs_ostream_write(buf,1,sz,out) == 0)
340      return -1;
341
342  return hpgs_istream_iserror(in);
343}
344