migrate substitution keywords to SVN
[reactos.git] / reactos / lib / kjs / src / iostream.c
1 /*
2 * I/O streams.
3 * Copyright (c) 1998 New Generation Software (NGS) Oy
4 *
5 * Author: Markku Rossi <mtr@ngs.fi>
6 */
7
8 /*
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * MA 02111-1307, USA
23 */
24
25 /*
26 * $Source: /cygdrive/c/RCVS/CVS/ReactOS/reactos/lib/kjs/src/iostream.c,v $
27 * $Id$
28 */
29
30 #include "jsint.h"
31
32 /*
33 * Types and definitions.
34 */
35
36 #define DEFAULT_BUFFER_SIZE 4096
37
38 /*
39 * Global functions.
40 */
41
42 JSIOStream *
43 js_iostream_new ()
44 {
45 JSIOStream *stream;
46
47 stream = js_calloc (NULL, 1, sizeof (*stream));
48 if (stream == NULL)
49 return NULL;
50
51 stream->buflen = DEFAULT_BUFFER_SIZE;
52 stream->buffer = js_malloc (NULL, stream->buflen);
53 if (stream->buffer == NULL)
54 {
55 js_free (stream);
56 return NULL;
57 }
58
59 return stream;
60 }
61
62
63 /* The `FILE *' stream. */
64
65 static int
66 file_read (void *context, unsigned char *buffer, unsigned int todo,
67 int *error_return)
68 {
69 int got;
70
71 errno = 0;
72 got = fread (buffer, 1, todo, (FILE *) context);
73 *error_return = errno;
74
75 return got;
76 }
77
78
79 static int
80 file_write (void *context, unsigned char *buffer, unsigned int todo,
81 int *error_return)
82 {
83 int wrote;
84
85 errno = 0;
86 wrote = fwrite (buffer, 1, todo, (FILE *) context);
87 *error_return = errno;
88
89 return wrote;
90 }
91
92
93 static int
94 file_seek (void *context, long offset, int whence)
95 {
96 return fseek ((FILE *) context, offset, whence);
97 }
98
99
100 static long
101 file_get_position (void *context)
102 {
103 return ftell ((FILE *) context);
104 }
105
106
107 static long
108 file_get_length (void *context)
109 {
110 FILE *fp = (FILE *) context;
111 long cpos;
112 long result = -1;
113
114 /* Save current position. */
115 cpos = ftell (fp);
116 if (cpos >= 0)
117 {
118 /* Seek to the end of the file. */
119 if (fseek (fp, 0, SEEK_END) >= 0)
120 {
121 /* Fetch result. */
122 result = ftell (fp);
123
124 /* Seek back. */
125 if (fseek (fp, cpos, SEEK_SET) < 0)
126 /* Couldn't revert the fp to the original position. */
127 result = -1;
128 }
129 }
130
131 return result;
132 }
133
134
135 static void
136 file_close (void *context)
137 {
138 fclose ((FILE *) context);
139 }
140
141
142 JSIOStream *
143 js_iostream_file (FILE *fp, int readp, int writep, int do_close)
144 {
145 JSIOStream *stream;
146
147 if (fp == NULL)
148 return NULL;
149
150 stream = js_iostream_new ();
151 if (stream == NULL)
152 return NULL;
153
154 if (readp)
155 stream->read = file_read;
156 if (writep)
157 stream->write = file_write;
158
159 stream->seek = file_seek;
160 stream->get_position = file_get_position;
161 stream->get_length = file_get_length;
162
163 if (do_close)
164 stream->close = file_close;
165
166 stream->context = fp;
167
168 return stream;
169 }
170
171
172 static void
173 close_pipe (void *context)
174 {
175 pclose ((FILE *) context);
176 }
177
178
179 JSIOStream *
180 js_iostream_pipe (FILE *fp, int readp)
181 {
182 JSIOStream *stream;
183
184 if (fp == NULL)
185 return NULL;
186
187 stream = js_iostream_new ();
188
189 if (stream == NULL)
190 return NULL;
191
192 if (readp)
193 stream->read = file_read;
194 else
195 stream->write = file_write;
196
197 stream->seek = file_seek;
198 stream->get_position = file_get_position;
199 stream->get_length = file_get_length;
200 stream->close = close_pipe;
201 stream->context = fp;
202
203 return stream;
204 }
205
206
207 size_t
208 js_iostream_read (JSIOStream *stream, void *ptr, size_t size)
209 {
210 size_t total = 0;
211 int got;
212
213 if (stream->writep)
214 {
215 /* We have buffered output data. */
216 if (js_iostream_flush (stream) == EOF)
217 return 0;
218
219 assert (stream->writep == 0);
220 }
221
222 while (size > 0)
223 {
224 /* First, take everything from the buffer. */
225 if (stream->bufpos < stream->data_in_buf)
226 {
227 got = stream->data_in_buf - stream->bufpos;
228
229 if (size < got)
230 got = size;
231
232 memcpy (ptr, stream->buffer + stream->bufpos, got);
233
234 stream->bufpos += got;
235 size -= got;
236 (unsigned char *) ptr += got;
237 total += got;
238 }
239 else
240 {
241 if (stream->at_eof)
242 /* EOF seen, can't read more. */
243 break;
244
245 js_iostream_fill_buffer (stream);
246 }
247 }
248
249 return total;
250 }
251
252
253 size_t
254 js_iostream_write (JSIOStream *stream, void *ptr, size_t size)
255 {
256 int space;
257 size_t total = 0;
258
259 if (stream->write == NULL)
260 {
261 stream->error = EBADF;
262 return 0;
263 }
264
265 if (!stream->writep && stream->bufpos < stream->data_in_buf)
266 {
267 /*
268 * We have some buffered data in the stream => the actual stream
269 * position in stream->context is not in sync with stream->bufpos.
270 * Seek back.
271 */
272
273 if ((*stream->seek) (stream->context, SEEK_CUR,
274 stream->bufpos - stream->data_in_buf) < 0)
275 /* XXX Error value. */
276 return 0;
277
278 stream->bufpos = 0;
279 stream->data_in_buf = 0;
280 }
281
282 while (size > 0)
283 {
284 space = stream->buflen - stream->data_in_buf;
285 if (size < space)
286 space = size;
287
288 /* Append data to the buffer. */
289 memcpy (stream->buffer + stream->data_in_buf, ptr, space);
290 stream->data_in_buf += space;
291 total += space;
292 size -= space;
293 (unsigned char *) ptr += space;
294
295 /* Now the buffer contains buffered write data. */
296 stream->writep = 1;
297
298 if (size > 0)
299 {
300 /* Still some data left. Must flush */
301 if (js_iostream_flush (stream) == EOF)
302 return total;
303 }
304 }
305
306 /* Autoflush. */
307 if (stream->autoflush && stream->writep)
308 if (js_iostream_flush (stream) == EOF)
309 /* Failed. Just return something smaller than <size> */
310 return total - stream->data_in_buf;
311
312 return total;
313 }
314
315
316 int
317 js_iostream_flush (JSIOStream *stream)
318 {
319 if (stream == NULL || stream->write == NULL || !stream->writep)
320 return 0;
321
322 stream->writep = 0;
323 assert (stream->bufpos == 0);
324
325 if (stream->data_in_buf > 0)
326 {
327 int to_write = stream->data_in_buf;
328
329 stream->data_in_buf = 0;
330 if ((*stream->write) (stream->context, stream->buffer,
331 to_write, &stream->error) < to_write)
332 {
333 stream->error = errno;
334 return EOF;
335 }
336 }
337
338 return 0;
339 }
340
341
342 int
343 js_iostream_unget (JSIOStream *stream, int byte)
344 {
345 if (stream->writep)
346 {
347 /* We have buffered output data. */
348 if (js_iostream_flush (stream) == EOF)
349 return EOF;
350
351 assert (stream->writep == 0);
352 }
353
354 if (stream->bufpos > 0)
355 {
356 /* It fits. */
357 stream->buffer[--stream->bufpos] = byte;
358 }
359 else if (stream->data_in_buf < stream->buflen)
360 {
361 move:
362 memmove (stream->buffer + 1, stream->buffer, stream->data_in_buf);
363 stream->data_in_buf++;
364 stream->buffer[0] = byte;
365 }
366 else
367 {
368 /* Allocate a bigger buffer. */
369 unsigned char *new_buffer = js_realloc (NULL, stream->buffer,
370 stream->buflen + 1);
371 if (new_buffer == NULL)
372 {
373 stream->error = errno;
374 return EOF;
375 }
376
377 stream->buflen++;
378 stream->buffer = new_buffer;
379 goto move;
380 }
381
382 /* Upon successful completion, we must return the byte. */
383 return byte;
384 }
385
386
387 int
388 js_iostream_close (JSIOStream *stream)
389 {
390 int result = 0;
391
392 if (stream == NULL)
393 return result;
394
395 if (js_iostream_flush (stream) == EOF)
396 result = EOF;
397
398 if (stream->close)
399 (*stream->close) (stream->context);
400
401 js_free (stream->buffer);
402 js_free (stream);
403
404 return result;
405 }
406
407
408 int
409 js_iostream_seek (JSIOStream *stream, long offset, int whence)
410 {
411 int result;
412
413 if (js_iostream_flush (stream) == EOF)
414 return -1;
415
416 result = (*stream->seek) (stream->context, offset, whence);
417 if (result == 0)
418 /* Successful. Clear the eof flag. */
419 stream->at_eof = 0;
420
421 return result;
422 }
423
424
425 long
426 js_iostream_get_position (JSIOStream *stream)
427 {
428 long pos;
429
430 /* Flush the possible buffered output. */
431 if (js_iostream_flush (stream) == EOF)
432 return -1;
433
434 pos = (*stream->get_position) (stream->context);
435 if (pos < 0)
436 return pos;
437
438 /*
439 * The logical position if at <bufpos>, the context's idea is at
440 * <data_in_buf>. Adjust.
441 */
442 return pos - (stream->data_in_buf - stream->bufpos);
443 }
444
445
446 long
447 js_iostream_get_length (JSIOStream *stream)
448 {
449 /* Flush the possible buffered output. */
450 if (js_iostream_flush (stream) == EOF)
451 return -1;
452
453 return (*stream->get_length) (stream->context);
454 }
455
456
457 void
458 js_iostream_fill_buffer (JSIOStream *stream)
459 {
460 if (stream->read == NULL)
461 {
462 stream->at_eof = 1;
463 return;
464 }
465
466 stream->data_in_buf = (*stream->read) (stream->context, stream->buffer,
467 stream->buflen, &stream->error);
468 stream->bufpos = 0;
469 if (stream->data_in_buf == 0)
470 stream->at_eof = 1;
471 }