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