]> Creatis software - gdcm.git/blob - src/gdcmjasper/src/libjasper/base/jas_stream.c
b28459cb19a2672725152068228ee960e0be3a5f
[gdcm.git] / src / gdcmjasper / src / libjasper / base / jas_stream.c
1 /*
2  * Copyright (c) 1999-2000 Image Power, Inc. and the University of
3  *   British Columbia.
4  * Copyright (c) 2001-2003 Michael David Adams.
5  * All rights reserved.
6  */
7
8 /* __START_OF_JASPER_LICENSE__
9  * 
10  * JasPer License Version 2.0
11  * 
12  * Copyright (c) 1999-2000 Image Power, Inc.
13  * Copyright (c) 1999-2000 The University of British Columbia
14  * Copyright (c) 2001-2003 Michael David Adams
15  * 
16  * All rights reserved.
17  * 
18  * Permission is hereby granted, free of charge, to any person (the
19  * "User") obtaining a copy of this software and associated documentation
20  * files (the "Software"), to deal in the Software without restriction,
21  * including without limitation the rights to use, copy, modify, merge,
22  * publish, distribute, and/or sell copies of the Software, and to permit
23  * persons to whom the Software is furnished to do so, subject to the
24  * following conditions:
25  * 
26  * 1.  The above copyright notices and this permission notice (which
27  * includes the disclaimer below) shall be included in all copies or
28  * substantial portions of the Software.
29  * 
30  * 2.  The name of a copyright holder shall not be used to endorse or
31  * promote products derived from the Software without specific prior
32  * written permission.
33  * 
34  * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS
35  * LICENSE.  NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
36  * THIS DISCLAIMER.  THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
37  * "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
38  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
39  * PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO
40  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
41  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
42  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
43  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
44  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  NO ASSURANCES ARE
45  * PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE
46  * THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY.
47  * EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS
48  * BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL
49  * PROPERTY RIGHTS OR OTHERWISE.  AS A CONDITION TO EXERCISING THE RIGHTS
50  * GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE
51  * ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY.  THE SOFTWARE
52  * IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL
53  * SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES,
54  * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL
55  * SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH
56  * THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH,
57  * PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH
58  * RISK ACTIVITIES").  THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY
59  * EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.
60  * 
61  * __END_OF_JASPER_LICENSE__
62  */
63
64 /*
65  * I/O Stream Library
66  *
67  * $Id: jas_stream.c,v 1.3 2005/06/11 14:23:07 malaterre Exp $
68  */
69
70 /******************************************************************************\
71 * Includes.
72 \******************************************************************************/
73
74 #include <assert.h>
75 #if defined(HAVE_FCNTL_H)
76 #include <fcntl.h>
77 #endif
78 #include <stdlib.h>
79 #include <stdarg.h>
80 #include <stdio.h>
81 #include <ctype.h>
82 #if defined(HAVE_UNISTD_H)
83 #include <unistd.h>
84 #endif
85 #if defined(WIN32) || defined(HAVE_IO_H)
86 #include <io.h>
87 #endif
88
89 #include "jasper/jas_types.h"
90 #include "jasper/jas_stream.h"
91 #include "jasper/jas_malloc.h"
92 #include "jasper/jas_math.h"
93
94 /******************************************************************************\
95 * Local function prototypes.
96 \******************************************************************************/
97
98 static int jas_strtoopenmode(const char *s);
99 static void jas_stream_destroy(jas_stream_t *stream);
100 static jas_stream_t *jas_stream_create(void);
101 static void jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf,
102   int bufsize);
103
104 static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt);
105 static int mem_write(jas_stream_obj_t *obj, char *buf, int cnt);
106 static long mem_seek(jas_stream_obj_t *obj, long offset, int origin);
107 static int mem_close(jas_stream_obj_t *obj);
108
109 static int sfile_read(jas_stream_obj_t *obj, char *buf, int cnt);
110 static int sfile_write(jas_stream_obj_t *obj, char *buf, int cnt);
111 static long sfile_seek(jas_stream_obj_t *obj, long offset, int origin);
112 static int sfile_close(jas_stream_obj_t *obj);
113
114 static int file_read(jas_stream_obj_t *obj, char *buf, int cnt);
115 static int file_write(jas_stream_obj_t *obj, char *buf, int cnt);
116 static long file_seek(jas_stream_obj_t *obj, long offset, int origin);
117 static int file_close(jas_stream_obj_t *obj);
118
119 /******************************************************************************\
120 * Local data.
121 \******************************************************************************/
122
123 static jas_stream_ops_t jas_stream_fileops = {
124   file_read,
125   file_write,
126   file_seek,
127   file_close
128 };
129
130 static jas_stream_ops_t jas_stream_sfileops = {
131   sfile_read,
132   sfile_write,
133   sfile_seek,
134   sfile_close
135 };
136
137 static jas_stream_ops_t jas_stream_memops = {
138   mem_read,
139   mem_write,
140   mem_seek,
141   mem_close
142 };
143
144 /******************************************************************************\
145 * Code for opening and closing streams.
146 \******************************************************************************/
147
148 static jas_stream_t *jas_stream_create()
149 {
150   jas_stream_t *stream;
151
152   if (!(stream = jas_malloc(sizeof(jas_stream_t)))) {
153     return 0;
154   }
155   stream->openmode_ = 0;
156   stream->bufmode_ = 0;
157   stream->flags_ = 0;
158   stream->bufbase_ = 0;
159   stream->bufstart_ = 0;
160   stream->bufsize_ = 0;
161   stream->ptr_ = 0;
162   stream->cnt_ = 0;
163   stream->ops_ = 0;
164   stream->obj_ = 0;
165   stream->rwcnt_ = 0;
166   stream->rwlimit_ = -1;
167
168   return stream;
169 }
170
171 JASGLOBAL(jas_stream_t) *jas_stream_memopen(char *buf, int bufsize)
172 {
173   jas_stream_t *stream;
174   jas_stream_memobj_t *obj;
175
176   if (!(stream = jas_stream_create())) {
177     return 0;
178   }
179
180   /* A stream associated with a memory buffer is always opened
181   for both reading and writing in binary mode. */
182   stream->openmode_ = JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
183
184   /* Since the stream data is already resident in memory, buffering
185   is not necessary. */
186   /* But... It still may be faster to use buffering anyways. */
187   jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
188
189   /* Select the operations for a memory stream. */
190   stream->ops_ = &jas_stream_memops;
191
192   /* Allocate memory for the underlying memory stream object. */
193   if (!(obj = jas_malloc(sizeof(jas_stream_memobj_t)))) {
194     jas_stream_destroy(stream);
195     return 0;
196   }
197   stream->obj_ = (void *) obj;
198
199   /* Initialize a few important members of the memory stream object. */
200   obj->myalloc_ = 0;
201   obj->buf_ = 0;
202
203   /* If the buffer size specified is nonpositive, then the buffer
204   is allocated internally and automatically grown as needed. */
205   if (bufsize <= 0) {
206     obj->bufsize_ = 1024;
207     obj->growable_ = 1;
208   } else {
209     obj->bufsize_ = bufsize;
210     obj->growable_ = 0;
211   }
212   if (buf) {
213     obj->buf_ = (unsigned char *) buf;
214   } else {
215     obj->buf_ = jas_malloc(obj->bufsize_ * sizeof(char));
216     obj->myalloc_ = 1;
217   }
218   if (!obj->buf_) {
219     jas_stream_close(stream);
220     return 0;
221   }
222
223   if (bufsize > 0 && buf) {
224     /* If a buffer was supplied by the caller and its length is positive,
225       make the associated buffer data appear in the stream initially. */
226     obj->len_ = bufsize;
227   } else {
228     /* The stream is initially empty. */
229     obj->len_ = 0;
230   }
231   obj->pos_ = 0;
232   
233   return stream;
234 }
235
236 jas_stream_t *jas_stream_fopen(const char *filename, const char *mode)
237 {
238   jas_stream_t *stream;
239   jas_stream_fileobj_t *obj;
240   int openflags;
241
242   /* Allocate a stream object. */
243   if (!(stream = jas_stream_create())) {
244     return 0;
245   }
246
247   /* Parse the mode string. */
248   stream->openmode_ = jas_strtoopenmode(mode);
249
250   /* Determine the correct flags to use for opening the file. */
251   if ((stream->openmode_ & JAS_STREAM_READ) &&
252     (stream->openmode_ & JAS_STREAM_WRITE)) {
253     openflags = O_RDWR;
254   } else if (stream->openmode_ & JAS_STREAM_READ) {
255     openflags = O_RDONLY;
256   } else if (stream->openmode_ & JAS_STREAM_WRITE) {
257     openflags = O_WRONLY;
258   } else {
259     openflags = 0;
260   }
261   if (stream->openmode_ & JAS_STREAM_APPEND) {
262     openflags |= O_APPEND;
263   }
264   if (stream->openmode_ & JAS_STREAM_BINARY) {
265     openflags |= O_BINARY;
266   }
267   if (stream->openmode_ & JAS_STREAM_CREATE) {
268     openflags |= O_CREAT | O_TRUNC;
269   }
270
271   /* Allocate space for the underlying file stream object. */
272   if (!(obj = jas_malloc(sizeof(jas_stream_fileobj_t)))) {
273     jas_stream_destroy(stream);
274     return 0;
275   }
276   obj->fd = -1;
277   obj->flags = 0;
278   obj->pathname[0] = '\0';
279   stream->obj_ = (void *) obj;
280
281   /* Select the operations for a file stream object. */
282   stream->ops_ = &jas_stream_fileops;
283
284   /* Open the underlying file. */
285   if ((obj->fd = open(filename, openflags, JAS_STREAM_PERMS)) < 0) {
286     jas_stream_destroy(stream);
287     return 0;
288   }
289
290   /* By default, use full buffering for this type of stream. */
291   jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
292
293   return stream;
294 }
295
296 jas_stream_t *jas_stream_freopen(const char *path, const char *mode, FILE *fp)
297 {
298   jas_stream_t *stream;
299   int openflags;
300
301   /* Eliminate compiler warning about unused variable. */
302   path = 0;
303
304   /* Allocate a stream object. */
305   if (!(stream = jas_stream_create())) {
306     return 0;
307   }
308
309   /* Parse the mode string. */
310   stream->openmode_ = jas_strtoopenmode(mode);
311
312   /* Determine the correct flags to use for opening the file. */
313   if ((stream->openmode_ & JAS_STREAM_READ) &&
314     (stream->openmode_ & JAS_STREAM_WRITE)) {
315     openflags = O_RDWR;
316   } else if (stream->openmode_ & JAS_STREAM_READ) {
317     openflags = O_RDONLY;
318   } else if (stream->openmode_ & JAS_STREAM_WRITE) {
319     openflags = O_WRONLY;
320   } else {
321     openflags = 0;
322   }
323   if (stream->openmode_ & JAS_STREAM_APPEND) {
324     openflags |= O_APPEND;
325   }
326   if (stream->openmode_ & JAS_STREAM_BINARY) {
327     openflags |= O_BINARY;
328   }
329   if (stream->openmode_ & JAS_STREAM_CREATE) {
330     openflags |= O_CREAT | O_TRUNC;
331   }
332
333   stream->obj_ = JAS_CAST(void *, fp);
334
335   /* Select the operations for a file stream object. */
336   stream->ops_ = &jas_stream_sfileops;
337
338   /* By default, use full buffering for this type of stream. */
339   jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
340
341   return stream;
342 }
343
344 jas_stream_t *jas_stream_tmpfile()
345 {
346   jas_stream_t *stream;
347   jas_stream_fileobj_t *obj;
348
349   if (!(stream = jas_stream_create())) {
350     return 0;
351   }
352
353   /* A temporary file stream is always opened for both reading and
354   writing in binary mode. */
355   stream->openmode_ = JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
356
357   /* Allocate memory for the underlying temporary file object. */
358   if (!(obj = jas_malloc(sizeof(jas_stream_fileobj_t)))) {
359     jas_stream_destroy(stream);
360     return 0;
361   }
362   obj->fd = -1;
363   obj->flags = 0;
364   obj->pathname[0] = '\0';
365   stream->obj_ = obj;
366
367   /* Choose a file name. */
368   tmpnam(obj->pathname);
369
370   /* Open the underlying file. */
371   if ((obj->fd = open(obj->pathname, O_CREAT | O_EXCL | O_RDWR | O_TRUNC | O_BINARY,
372     JAS_STREAM_PERMS)) < 0) {
373     jas_stream_destroy(stream);
374     return 0;
375   }
376
377   /* Unlink the file so that it will disappear if the program
378   terminates abnormally. */
379   /* Under UNIX, one can unlink an open file and continue to do I/O
380   on it.  Not all operating systems support this functionality, however.
381   For example, under Microsoft Windows the unlink operation will fail,
382   since the file is open. */
383   if (unlink(obj->pathname)) {
384     /* We will try unlinking the file again after it is closed. */
385     obj->flags |= JAS_STREAM_FILEOBJ_DELONCLOSE;
386   }
387
388   /* Use full buffering. */
389   jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
390
391   stream->ops_ = &jas_stream_fileops;
392
393   return stream;
394 }
395
396 jas_stream_t *jas_stream_fdopen(int fd, const char *mode)
397 {
398   jas_stream_t *stream;
399   jas_stream_fileobj_t *obj;
400
401   /* Allocate a stream object. */
402   if (!(stream = jas_stream_create())) {
403     return 0;
404   }
405
406   /* Parse the mode string. */
407   stream->openmode_ = jas_strtoopenmode(mode);
408
409 #if defined(WIN32)
410   /* Argh!!!  Someone ought to banish text mode (i.e., O_TEXT) to the
411     greatest depths of purgatory! */
412   /* Ensure that the file descriptor is in binary mode, if the caller
413     has specified the binary mode flag.  Arguably, the caller ought to
414     take care of this, but text mode is a ugly wart anyways, so we save
415     the caller some grief by handling this within the stream library. */
416   /* This ugliness is mainly for the benefit of those who run the
417     JasPer software under Windows from shells that insist on opening
418     files in text mode.  For example, in the Cygwin environment,
419     shells often open files in text mode when I/O redirection is
420     used.  Grr... */
421   if (stream->openmode_ & JAS_STREAM_BINARY) {
422     setmode(fd, O_BINARY);
423   }
424 #endif
425
426   /* Allocate space for the underlying file stream object. */
427   if (!(obj = jas_malloc(sizeof(jas_stream_fileobj_t)))) {
428     jas_stream_destroy(stream);
429     return 0;
430   }
431   obj->fd = fd;
432   obj->flags = 0;
433   obj->pathname[0] = '\0';
434   stream->obj_ = (void *) obj;
435
436   /* Do not close the underlying file descriptor when the stream is
437   closed. */
438   obj->flags |= JAS_STREAM_FILEOBJ_NOCLOSE;
439
440   /* By default, use full buffering for this type of stream. */
441   jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
442
443   /* Select the operations for a file stream object. */
444   stream->ops_ = &jas_stream_fileops;
445
446   return stream;
447 }
448
449 static void jas_stream_destroy(jas_stream_t *stream)
450 {
451   /* If the memory for the buffer was allocated with malloc, free
452   this memory. */
453   if ((stream->bufmode_ & JAS_STREAM_FREEBUF) && stream->bufbase_) {
454     jas_free(stream->bufbase_);
455     stream->bufbase_ = 0;
456   }
457   jas_free(stream);
458 }
459
460 JASGLOBAL(int) jas_stream_close(jas_stream_t *stream)
461 {
462   /* Flush buffer if necessary. */
463   jas_stream_flush(stream);
464
465   /* Close the underlying stream object. */
466   (*stream->ops_->close_)(stream->obj_);
467
468   jas_stream_destroy(stream);
469
470   return 0;
471 }
472
473 /******************************************************************************\
474 * Code for reading and writing streams.
475 \******************************************************************************/
476
477 int jas_stream_getc_func(jas_stream_t *stream)
478 {
479   assert(stream->ptr_ - stream->bufbase_ <= stream->bufsize_ +
480     JAS_STREAM_MAXPUTBACK);
481   return jas_stream_getc_macro(stream);
482 }
483
484 int jas_stream_putc_func(jas_stream_t *stream, int c)
485 {
486   assert(stream->ptr_ - stream->bufstart_ <= stream->bufsize_);
487   return jas_stream_putc_macro(stream, c);
488 }
489
490 int jas_stream_ungetc(jas_stream_t *stream, int c)
491 {
492   if (!stream->ptr_ || stream->ptr_ == stream->bufbase_) {
493     return -1;
494   }
495
496   /* Reset the EOF indicator (since we now have at least one character
497     to read). */
498   stream->flags_ &= ~JAS_STREAM_EOF;
499
500   --stream->rwcnt_;
501   --stream->ptr_;
502   ++stream->cnt_;
503   *stream->ptr_ = c;
504   return 0;
505 }
506
507 int jas_stream_read(jas_stream_t *stream, void *buf, int cnt)
508 {
509   int n;
510   int c;
511   char *bufptr;
512
513   bufptr = buf;
514
515   n = 0;
516   while (n < cnt) {
517     if ((c = jas_stream_getc(stream)) == EOF) {
518       return n;
519     }
520     *bufptr++ = c;
521     ++n;
522   }
523
524   return n;
525 }
526
527 int jas_stream_write(jas_stream_t *stream, const void *buf, int cnt)
528 {
529   int n;
530   const char *bufptr;
531
532   bufptr = buf;
533
534   n = 0;
535   while (n < cnt) {
536     if (jas_stream_putc(stream, *bufptr) == EOF) {
537       return n;
538     }
539     ++bufptr;
540     ++n;
541   }
542
543   return n;
544 }
545
546 /* Note: This function uses a fixed size buffer.  Therefore, it cannot
547   handle invocations that will produce more output than can be held
548   by the buffer. */
549 int jas_stream_printf(jas_stream_t *stream, const char *fmt, ...)
550 {
551   va_list ap;
552   char buf[4096];
553   int ret;
554
555   va_start(ap, fmt);
556   ret = vsprintf(buf, fmt, ap);
557   jas_stream_puts(stream, buf);
558   va_end(ap);
559   return ret;
560 }
561
562 int jas_stream_puts(jas_stream_t *stream, const char *s)
563 {
564   while (*s != '\0') {
565     if (jas_stream_putc_macro(stream, *s) == EOF) {
566       return -1;
567     }
568     ++s;
569   }
570   return 0;
571 }
572
573 char *jas_stream_gets(jas_stream_t *stream, char *buf, int bufsize)
574 {
575   int c;
576   char *bufptr;
577   assert(bufsize > 0);
578
579   bufptr = buf;
580   while (bufsize > 1) {
581     if ((c = jas_stream_getc(stream)) == EOF) {
582       break;
583     }
584     *bufptr++ = c;
585     --bufsize;
586     if (c == '\n') {
587       break;
588     }
589   }
590   *bufptr = '\0';
591   return buf;
592 }
593
594 int jas_stream_gobble(jas_stream_t *stream, int n)
595 {
596   int m;
597   m = n;
598   for (m = n; m > 0; --m) {
599     if (jas_stream_getc(stream) == EOF) {
600       return n - m;
601     }
602   }
603   return n;
604 }
605
606 int jas_stream_pad(jas_stream_t *stream, int n, int c)
607 {
608   int m;
609   m = n;
610   for (m = n; m > 0; --m) {
611     if (jas_stream_putc(stream, c) == EOF)
612       return n - m;
613   }
614   return n;
615 }
616
617 /******************************************************************************\
618 * Code for getting and setting the stream position.
619 \******************************************************************************/
620
621 int jas_stream_isseekable(jas_stream_t *stream)
622 {
623   if (stream->ops_ == &jas_stream_memops) {
624     return 1;
625   } else if (stream->ops_ == &jas_stream_fileops) {
626     if ((*stream->ops_->seek_)(stream->obj_, 0, SEEK_CUR) < 0) {
627       return 0;
628     }
629     return 1;
630   } else {
631     return 0;
632   }
633 }
634
635 int jas_stream_rewind(jas_stream_t *stream)
636 {
637   return jas_stream_seek(stream, 0, SEEK_SET);
638 }
639
640 long jas_stream_seek(jas_stream_t *stream, long offset, int origin)
641 {
642   long newpos;
643
644   /* The buffer cannot be in use for both reading and writing. */
645   assert(!((stream->bufmode_ & JAS_STREAM_RDBUF) && (stream->bufmode_ &
646     JAS_STREAM_WRBUF)));
647
648   /* Reset the EOF indicator (since we may not be at the EOF anymore). */
649   stream->flags_ &= ~JAS_STREAM_EOF;
650
651   if (stream->bufmode_ & JAS_STREAM_RDBUF) {
652     if (origin == SEEK_CUR) {
653       offset -= stream->cnt_;
654     }
655   } else if (stream->bufmode_ & JAS_STREAM_WRBUF) {
656     if (jas_stream_flush(stream)) {
657       return -1;
658     }
659   }
660   stream->cnt_ = 0;
661   stream->ptr_ = stream->bufstart_;
662   stream->bufmode_ &= ~(JAS_STREAM_RDBUF | JAS_STREAM_WRBUF);
663
664   if ((newpos = (*stream->ops_->seek_)(stream->obj_, offset, origin))
665     < 0) {
666     return -1;
667   }
668
669   return newpos;
670 }
671
672 long jas_stream_tell(jas_stream_t *stream)
673 {
674   int adjust;
675   int offset;
676
677   if (stream->bufmode_ & JAS_STREAM_RDBUF) {
678     adjust = -stream->cnt_;
679   } else if (stream->bufmode_ & JAS_STREAM_WRBUF) {
680     adjust = stream->ptr_ - stream->bufstart_;
681   } else {
682     adjust = 0;
683   }
684
685   if ((offset = (*stream->ops_->seek_)(stream->obj_, 0, SEEK_CUR)) < 0) {
686     return -1;
687   }
688
689   return offset + adjust;
690 }
691
692 /******************************************************************************\
693 * Buffer initialization code.
694 \******************************************************************************/
695
696 static void jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf,
697   int bufsize)
698 {
699   /* If this function is being called, the buffer should not have been
700     initialized yet. */
701   assert(!stream->bufbase_);
702
703   if (bufmode != JAS_STREAM_UNBUF) {
704     /* The full- or line-buffered mode is being employed. */
705     if (!buf) {
706       /* The caller has not specified a buffer to employ, so allocate
707         one. */
708       if ((stream->bufbase_ = jas_malloc(JAS_STREAM_BUFSIZE +
709         JAS_STREAM_MAXPUTBACK))) {
710         stream->bufmode_ |= JAS_STREAM_FREEBUF;
711         stream->bufsize_ = JAS_STREAM_BUFSIZE;
712       } else {
713         /* The buffer allocation has failed.  Resort to unbuffered
714           operation. */
715         stream->bufbase_ = stream->tinybuf_;
716         stream->bufsize_ = 1;
717       }
718     } else {
719       /* The caller has specified a buffer to employ. */
720       /* The buffer must be large enough to accommodate maximum
721         putback. */
722       assert(bufsize > JAS_STREAM_MAXPUTBACK);
723       stream->bufbase_ = JAS_CAST(uchar *, buf);
724       stream->bufsize_ = bufsize - JAS_STREAM_MAXPUTBACK;
725     }
726   } else {
727     /* The unbuffered mode is being employed. */
728     /* A buffer should not have been supplied by the caller. */
729     assert(!buf);
730     /* Use a trivial one-character buffer. */
731     stream->bufbase_ = stream->tinybuf_;
732     stream->bufsize_ = 1;
733   }
734   stream->bufstart_ = &stream->bufbase_[JAS_STREAM_MAXPUTBACK];
735   stream->ptr_ = stream->bufstart_;
736   stream->cnt_ = 0;
737   stream->bufmode_ |= bufmode & JAS_STREAM_BUFMODEMASK;
738 }
739
740 /******************************************************************************\
741 * Buffer filling and flushing code.
742 \******************************************************************************/
743
744 int jas_stream_flush(jas_stream_t *stream)
745 {
746   if (stream->bufmode_ & JAS_STREAM_RDBUF) {
747     return 0;
748   }
749   return jas_stream_flushbuf(stream, EOF);
750 }
751
752 int jas_stream_fillbuf(jas_stream_t *stream, int getflag)
753 {
754   int c;
755
756   /* The stream must not be in an error or EOF state. */
757   if ((stream->flags_ & (JAS_STREAM_ERRMASK)) != 0) {
758     return EOF;
759   }
760
761   /* The stream must be open for reading. */
762   if ((stream->openmode_ & JAS_STREAM_READ) == 0) {
763     return EOF;
764   }
765
766   /* Make a half-hearted attempt to confirm that the buffer is not
767   currently being used for writing.  This check is not intended
768   to be foolproof! */
769   assert((stream->bufmode_ & JAS_STREAM_WRBUF) == 0);
770
771   assert(stream->ptr_ - stream->bufstart_ <= stream->bufsize_);
772
773   /* Mark the buffer as being used for reading. */
774   stream->bufmode_ |= JAS_STREAM_RDBUF;
775
776   /* Read new data into the buffer. */
777   stream->ptr_ = stream->bufstart_;
778   if ((stream->cnt_ = (*stream->ops_->read_)(stream->obj_,
779     (char *) stream->bufstart_, stream->bufsize_)) <= 0) {
780     if (stream->cnt_ < 0) {
781       stream->flags_ |= JAS_STREAM_ERR;
782     } else {
783       stream->flags_ |= JAS_STREAM_EOF;
784     }
785     stream->cnt_ = 0;
786     return EOF;
787   }
788
789   assert(stream->cnt_ > 0);
790   /* Get or peek at the first character in the buffer. */
791   c = (getflag) ? jas_stream_getc2(stream) : (*stream->ptr_);
792
793   return c;
794 }
795
796 int jas_stream_flushbuf(jas_stream_t *stream, int c)
797 {
798   int len;
799   int n;
800
801   /* The stream should not be in an error or EOF state. */
802   if ((stream->flags_ & (JAS_STREAM_ERRMASK)) != 0) {
803     return EOF;
804   }
805
806   /* The stream must be open for writing. */
807   if ((stream->openmode_ & (JAS_STREAM_WRITE | JAS_STREAM_APPEND)) == 0) {
808     return EOF;
809   }
810
811   /* The buffer should not currently be in use for reading. */
812   assert(!(stream->bufmode_ & JAS_STREAM_RDBUF));
813
814   /* Note: Do not use the quantity stream->cnt to determine the number
815   of characters in the buffer!  Depending on how this function was
816   called, the stream->cnt value may be "off-by-one". */
817   len = stream->ptr_ - stream->bufstart_;
818   if (len > 0) {
819     n = (*stream->ops_->write_)(stream->obj_, (char *)
820       stream->bufstart_, len);
821     if (n != len) {
822       stream->flags_ |= JAS_STREAM_ERR;
823       return EOF;
824     }
825   }
826   stream->cnt_ = stream->bufsize_;
827   stream->ptr_ = stream->bufstart_;
828
829   stream->bufmode_ |= JAS_STREAM_WRBUF;
830
831   if (c != EOF) {
832     assert(stream->cnt_ > 0);
833     return jas_stream_putc2(stream, c);
834   }
835
836   return 0;
837 }
838
839 /******************************************************************************\
840 * Miscellaneous code.
841 \******************************************************************************/
842
843 static int jas_strtoopenmode(const char *s)
844 {
845   int openmode = 0;
846   while (*s != '\0') {
847     switch (*s) {
848     case 'r':
849       openmode |= JAS_STREAM_READ;
850       break;
851     case 'w':
852       openmode |= JAS_STREAM_WRITE | JAS_STREAM_CREATE;
853       break;
854     case 'b':
855       openmode |= JAS_STREAM_BINARY;
856       break;
857     case 'a':
858       openmode |= JAS_STREAM_APPEND;
859       break;
860     case '+':
861       openmode |= JAS_STREAM_READ | JAS_STREAM_WRITE;
862       break;
863     default:
864       break;
865     }
866     ++s;
867   }
868   return openmode;
869 }
870
871 int jas_stream_copy(jas_stream_t *out, jas_stream_t *in, int n)
872 {
873   int all;
874   int c;
875   int m;
876
877   all = (n < 0) ? 1 : 0;
878
879   m = n;
880   while (all || m > 0) {
881     if ((c = jas_stream_getc_macro(in)) == EOF) {
882       /* The next character of input could not be read. */
883       /* Return with an error if an I/O error occured
884         (not including EOF) or if an explicit copy count
885         was specified. */
886       return (!all || jas_stream_error(in)) ? (-1) : 0;
887     }
888     if (jas_stream_putc_macro(out, c) == EOF) {
889       return -1;
890     }
891     --m;
892   }
893   return 0;
894 }
895
896 long jas_stream_setrwcount(jas_stream_t *stream, long rwcnt)
897 {
898   int old;
899
900   old = stream->rwcnt_;
901   stream->rwcnt_ = rwcnt;
902   return old;
903 }
904
905 int jas_stream_display(jas_stream_t *stream, FILE *fp, int n)
906 {
907   unsigned char buf[16];
908   int i;
909   int j;
910   int m;
911   int c;
912   int display;
913   int cnt;
914
915   cnt = n - (n % 16);
916   display = 1;
917
918   for (i = 0; i < n; i += 16) {
919     if (n > 16 && i > 0) {
920       display = (i >= cnt) ? 1 : 0;
921     }
922     if (display) {
923       fprintf(fp, "%08x:", i);
924     }
925     m = JAS_MIN(n - i, 16);
926     for (j = 0; j < m; ++j) {
927       if ((c = jas_stream_getc(stream)) == EOF) {
928         abort();
929         return -1;
930       }
931       buf[j] = c;
932     }
933     if (display) {
934       for (j = 0; j < m; ++j) {
935         fprintf(fp, " %02x", buf[j]);
936       }
937       fputc(' ', fp);
938       for (; j < 16; ++j) {
939         fprintf(fp, "   ");
940       }
941       for (j = 0; j < m; ++j) {
942         if (isprint(buf[j])) {
943           fputc(buf[j], fp);
944         } else {
945           fputc(' ', fp);
946         }
947       }
948       fprintf(fp, "\n");
949     }
950
951
952   }
953   return 0;
954 }
955
956 long jas_stream_length(jas_stream_t *stream)
957 {
958   long oldpos;
959   long pos;
960   if ((oldpos = jas_stream_tell(stream)) < 0) {
961     return -1;
962   }
963   if (jas_stream_seek(stream, 0, SEEK_END) < 0) {
964     return -1;
965   }
966   if ((pos = jas_stream_tell(stream)) < 0) {
967     return -1;
968   }
969   if (jas_stream_seek(stream, oldpos, SEEK_SET) < 0) {
970     return -1;
971   }
972   return pos;
973 }
974
975 /******************************************************************************\
976 * Memory stream object.
977 \******************************************************************************/
978
979 static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt)
980 {
981   int n;
982   jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
983   n = m->len_ - m->pos_;
984   cnt = JAS_MIN(n, cnt);
985   memcpy(buf, &m->buf_[m->pos_], cnt);
986   m->pos_ += cnt;
987   return cnt;
988 }
989
990 static int mem_resize(jas_stream_memobj_t *m, int bufsize)
991 {
992   unsigned char *buf;
993
994   assert(m->buf_);
995   if (!(buf = jas_realloc(m->buf_, bufsize * sizeof(unsigned char)))) {
996     return -1;
997   }
998   m->buf_ = buf;
999   m->bufsize_ = bufsize;
1000   return 0;
1001 }
1002
1003 static int mem_write(jas_stream_obj_t *obj, char *buf, int cnt)
1004 {
1005   int n;
1006   int ret;
1007   jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
1008   long newbufsize;
1009   long newpos;
1010
1011   newpos = m->pos_ + cnt;
1012   if (newpos > m->bufsize_ && m->growable_) {
1013     newbufsize = m->bufsize_;
1014     while (newbufsize < newpos) {
1015       newbufsize <<= 1;
1016       assert(newbufsize >= 0);
1017     }
1018     if (mem_resize(m, newbufsize)) {
1019       return -1;
1020     }
1021   }
1022   if (m->pos_ > m->len_) {
1023     /* The current position is beyond the end of the file, so
1024       pad the file to the current position with zeros. */
1025     n = JAS_MIN(m->pos_, m->bufsize_) - m->len_;
1026     if (n > 0) {
1027       memset(&m->buf_[m->len_], 0, n);
1028       m->len_ += n;
1029     }
1030     if (m->pos_ != m->len_) {
1031       /* The buffer is not big enough. */
1032       return 0;
1033     }
1034   }
1035   n = m->bufsize_ - m->pos_;
1036   ret = JAS_MIN(n, cnt);
1037   if (ret > 0) {
1038     memcpy(&m->buf_[m->pos_], buf, ret);
1039     m->pos_ += ret;
1040   }
1041   if (m->pos_ > m->len_) {
1042     m->len_ = m->pos_;
1043   }
1044 assert(ret == cnt);
1045   return ret;
1046 }
1047
1048 static long mem_seek(jas_stream_obj_t *obj, long offset, int origin)
1049 {
1050   jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
1051   long newpos;
1052
1053   switch (origin) {
1054   case SEEK_SET:
1055     newpos = offset;
1056     break;
1057   case SEEK_END:
1058     newpos = m->len_ - offset;
1059     break;
1060   case SEEK_CUR:
1061     newpos = m->pos_ + offset;
1062     break;
1063   default:
1064     abort();
1065     break;
1066   }
1067   if (newpos < 0) {
1068     return -1;
1069   }
1070   m->pos_ = newpos;
1071
1072   return m->pos_;
1073 }
1074
1075 static int mem_close(jas_stream_obj_t *obj)
1076 {
1077   jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
1078   if (m->myalloc_ && m->buf_) {
1079     jas_free(m->buf_);
1080     m->buf_ = 0;
1081   }
1082   jas_free(obj);
1083   return 0;
1084 }
1085
1086 /******************************************************************************\
1087 * File stream object.
1088 \******************************************************************************/
1089
1090 static int file_read(jas_stream_obj_t *obj, char *buf, int cnt)
1091 {
1092   jas_stream_fileobj_t *fileobj = JAS_CAST(jas_stream_fileobj_t *, obj);
1093   return read(fileobj->fd, buf, cnt);
1094 }
1095
1096 static int file_write(jas_stream_obj_t *obj, char *buf, int cnt)
1097 {
1098   jas_stream_fileobj_t *fileobj = JAS_CAST(jas_stream_fileobj_t *, obj);
1099   return write(fileobj->fd, buf, cnt);
1100 }
1101
1102 static long file_seek(jas_stream_obj_t *obj, long offset, int origin)
1103 {
1104   jas_stream_fileobj_t *fileobj = JAS_CAST(jas_stream_fileobj_t *, obj);
1105   return lseek(fileobj->fd, offset, origin);
1106 }
1107
1108 static int file_close(jas_stream_obj_t *obj)
1109 {
1110   jas_stream_fileobj_t *fileobj = JAS_CAST(jas_stream_fileobj_t *, obj);
1111   int ret;
1112   ret = close(fileobj->fd);
1113   if (fileobj->flags & JAS_STREAM_FILEOBJ_DELONCLOSE) {
1114     unlink(fileobj->pathname);
1115   }
1116   jas_free(fileobj);
1117   return ret;
1118 }
1119
1120 /******************************************************************************\
1121 * Stdio file stream object.
1122 \******************************************************************************/
1123
1124 static int sfile_read(jas_stream_obj_t *obj, char *buf, int cnt)
1125 {
1126   FILE *fp;
1127   fp = JAS_CAST(FILE *, obj);
1128   return fread(buf, 1, cnt, fp);
1129 }
1130
1131 static int sfile_write(jas_stream_obj_t *obj, char *buf, int cnt)
1132 {
1133   FILE *fp;
1134   fp = JAS_CAST(FILE *, obj);
1135   return fwrite(buf, 1, cnt, fp);
1136 }
1137
1138 static long sfile_seek(jas_stream_obj_t *obj, long offset, int origin)
1139 {
1140   FILE *fp;
1141   fp = JAS_CAST(FILE *, obj);
1142   return fseek(fp, offset, origin);
1143 }
1144
1145 static int sfile_close(jas_stream_obj_t *obj)
1146 {
1147   FILE *fp;
1148   fp = JAS_CAST(FILE *, obj);
1149   return fclose(fp);
1150 }