summaryrefslogtreecommitdiffstats
path: root/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu/ByteStream.cpp')
-rw-r--r--kviewshell/plugins/djvu/libdjvu/ByteStream.cpp1445
1 files changed, 1445 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp
new file mode 100644
index 00000000..011b348d
--- /dev/null
+++ b/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp
@@ -0,0 +1,1445 @@
+//C- -*- C++ -*-
+//C- -------------------------------------------------------------------
+//C- DjVuLibre-3.5
+//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun.
+//C- Copyright (c) 2001 AT&T
+//C-
+//C- This software is subject to, and may be distributed under, the
+//C- GNU General Public License, Version 2. The license should have
+//C- accompanied the software or you may obtain a copy of the license
+//C- from the Free Software Foundation at http://www.fsf.org .
+//C-
+//C- This program is distributed in the hope that it will be useful,
+//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
+//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//C- GNU General Public License for more details.
+//C-
+//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
+//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech
+//C- Software authorized us to replace the original DjVu(r) Reference
+//C- Library notice by the following text (see doc/lizard2002.djvu):
+//C-
+//C- ------------------------------------------------------------------
+//C- | DjVu (r) Reference Library (v. 3.5)
+//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
+//C- | The DjVu Reference Library is protected by U.S. Pat. No.
+//C- | 6,058,214 and patents pending.
+//C- |
+//C- | This software is subject to, and may be distributed under, the
+//C- | GNU General Public License, Version 2. The license should have
+//C- | accompanied the software or you may obtain a copy of the license
+//C- | from the Free Software Foundation at http://www.fsf.org .
+//C- |
+//C- | The computer code originally released by LizardTech under this
+//C- | license and unmodified by other parties is deemed "the LIZARDTECH
+//C- | ORIGINAL CODE." Subject to any third party intellectual property
+//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
+//C- | non-exclusive license to make, use, sell, or otherwise dispose of
+//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
+//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
+//C- | General Public License. This grant only confers the right to
+//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
+//C- | the extent such infringement is reasonably necessary to enable
+//C- | recipient to make, have made, practice, sell, or otherwise dispose
+//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
+//C- | any greater extent that may be necessary to utilize further
+//C- | modifications or combinations.
+//C- |
+//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
+//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
+//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+//C- +------------------------------------------------------------------
+//
+// $Id: ByteStream.cpp,v 1.18 2004/08/06 14:50:05 leonb Exp $
+// $Name: release_3_5_15 $
+
+// From: Leon Bottou, 1/31/2002
+// This file has very little to do with my initial implementation.
+// It has been practically rewritten by Lizardtech for i18n changes.
+// Our original implementation consisted of multiple classes.
+// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>.
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#if NEED_GNUG_PRAGMAS
+# pragma implementation
+#endif
+
+// - Author: Leon Bottou, 04/1997
+
+#include "DjVuGlobal.h"
+#include "ByteStream.h"
+#include "GOS.h"
+#include "GURL.h"
+#include "DjVuMessage.h"
+#include <fcntl.h>
+#if defined(WIN32) || defined(__CYGWIN32__)
+# include <io.h>
+#endif
+
+#ifdef UNIX
+# ifndef HAS_MEMMAP
+# define HAS_MEMMAP 1
+# endif
+#endif
+
+#if defined(UNIX)
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <errno.h>
+# ifdef HAS_MEMMAP
+# include <sys/mman.h>
+# endif
+#elif defined(macintosh)
+# include <unistd.h>
+_MSL_IMP_EXP_C int _dup(int);
+_MSL_IMP_EXP_C int _dup2(int,int);
+_MSL_IMP_EXP_C int _close(int);
+__inline int dup(int _a ) { return _dup(_a);}
+__inline int dup2(int _a, int _b ) { return _dup2(_a, _b);}
+#endif
+
+#ifdef HAVE_NAMESPACES
+namespace DJVU {
+# ifdef NOT_DEFINED // Just to fool emacs c++ mode
+}
+#endif
+#endif
+
+const char *ByteStream::EndOfFile=ERR_MSG("EOF");
+
+/** ByteStream interface for stdio files.
+ The virtual member functions #read#, #write#, #tell# and #seek# are mapped
+ to the well known stdio functions #fread#, #fwrite#, #ftell# and #fseek#.
+ @see Unix man page fopen(3), fread(3), fwrite(3), ftell(3), fseek(3) */
+
+class ByteStream::Stdio : public ByteStream {
+public:
+ Stdio(void);
+
+ /** Constructs a ByteStream for accessing the file named #url#.
+ Arguments #url# and #mode# are similar to the arguments of the well
+ known stdio function #fopen#. In addition a url of #-# will be
+ interpreted as the standard output or the standard input according to
+ #mode#. This constructor will open a stdio file and construct a
+ ByteStream object accessing this file. Destroying the ByteStream object
+ will flush and close the associated stdio file. Returns an error code
+ if the stdio file cannot be opened. */
+ GUTF8String init(const GURL &url, const char * const mode);
+
+ /** Constructs a ByteStream for accessing the stdio file #f#.
+ Argument #mode# indicates the type of the stdio file, as in the
+ well known stdio function #fopen#. Destroying the ByteStream
+ object will not close the stdio file #f# unless closeme is true. */
+ GUTF8String init(FILE * const f, const char * const mode="rb", const bool closeme=false);
+
+ /** Initializes from stdio */
+ GUTF8String init(const char mode[]);
+
+ // Virtual functions
+ ~Stdio();
+ virtual size_t read(void *buffer, size_t size);
+ virtual size_t write(const void *buffer, size_t size);
+ virtual void flush(void);
+ virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false);
+ virtual long tell(void) const;
+private:
+ // Cancel C++ default stuff
+ Stdio(const Stdio &);
+ Stdio & operator=(const Stdio &);
+private:
+ // Implementation
+ bool can_read;
+ bool can_write;
+ bool must_close;
+protected:
+ FILE *fp;
+ long pos;
+};
+
+inline GUTF8String
+ByteStream::Stdio::init(FILE * const f,const char mode[],const bool closeme)
+{
+ fp=f;
+ must_close=closeme;
+ return init(mode);
+}
+
+
+/** ByteStream interface managing a memory buffer.
+ Class #ByteStream::Memory# manages a dynamically resizable buffer from
+ which data can be read or written. The buffer itself is organized as an
+ array of blocks of 4096 bytes. */
+
+class ByteStream::Memory : public ByteStream
+{
+public:
+ /** Constructs an empty ByteStream::Memory.
+ The buffer is initially empty. You must first use function #write#
+ to store data into the buffer, use function #seek# to rewind the
+ current position, and function #read# to read the data back. */
+ Memory();
+ /** Constructs a Memory by copying initial data. The
+ Memory buffer is initialized with #size# bytes copied from the
+ memory area pointed to by #buffer#. */
+ GUTF8String init(const void * const buffer, const size_t size);
+ // Virtual functions
+ ~Memory();
+ virtual size_t read(void *buffer, size_t size);
+ virtual size_t write(const void *buffer, size_t size);
+ virtual int seek(long offset, int whence=SEEK_SET, bool nothrow=false);
+ virtual long tell(void) const;
+ /** Erases everything in the Memory.
+ The current location is reset to zero. */
+ void empty();
+ /** Returns the total number of bytes contained in the buffer. Valid
+ offsets for function #seek# range from 0 to the value returned by this
+ function. */
+ virtual int size(void) const;
+ /** Returns a reference to the byte at offset #n#. This reference can be
+ used to read (as in #mbs[n]#) or modify (as in #mbs[n]=c#) the contents
+ of the buffer. */
+ char &operator[] (int n);
+ /** Copies all internal data into \Ref{TArray} and returns it */
+private:
+ // Cancel C++ default stuff
+ Memory(const Memory &);
+ Memory & operator=(const Memory &);
+ // Current position
+ int where;
+protected:
+ /** Reads data from a random position. This function reads at most #sz#
+ bytes at position #pos# into #buffer# and returns the actual number of
+ bytes read. The current position is unchanged. */
+ virtual size_t readat(void *buffer, size_t sz, int pos);
+ /** Number of bytes in internal buffer. */
+ int bsize;
+ /** Number of 4096 bytes blocks. */
+ int nblocks;
+ /** Pointers (possibly null) to 4096 bytes blocks. */
+ char **blocks;
+ /** Pointers (possibly null) to 4096 bytes blocks. */
+ GPBuffer<char *> gblocks;
+};
+
+
+
+inline int
+ByteStream::Memory::size(void) const
+{
+ return bsize;
+}
+
+inline char &
+ByteStream::Memory::operator[] (int n)
+{
+ return blocks[n>>12][n&0xfff];
+}
+
+
+
+/** Read-only ByteStream interface to a memory area.
+ Class #ByteStream::Static# implements a read-only ByteStream interface for a
+ memory area specified by the user at construction time. Calls to function
+ #read# directly access this memory area. The user must therefore make
+ sure that its content remain valid long enough. */
+
+class ByteStream::Static : public ByteStream
+{
+public:
+ class Allocate;
+ class Duplicate;
+ friend class Duplicate;
+
+ /** Creates a Static object for allocating the memory area of
+ length #sz# starting at address #buffer#. */
+ Static(const void * const buffer, const size_t sz);
+ ~Static();
+ // Virtual functions
+ virtual size_t read(void *buffer, size_t sz);
+ virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false);
+ virtual long tell(void) const;
+ /** Returns the total number of bytes contained in the buffer, file, etc.
+ Valid offsets for function #seek# range from 0 to the value returned
+ by this function. */
+ virtual int size(void) const;
+ virtual GP<ByteStream> duplicate(const size_t xsize) const;
+ /// Returns false, unless a subclass of ByteStream::Static
+ virtual bool is_static(void) const { return true; }
+protected:
+ const char *data;
+ int bsize;
+private:
+ int where;
+};
+
+ByteStream::Static::~Static() {}
+
+class ByteStream::Static::Allocate : public ByteStream::Static
+{
+public:
+ friend class ByteStream;
+protected:
+ char *buf;
+ GPBuffer<char> gbuf;
+public:
+ Allocate(const size_t size) : Static(0,size), gbuf(buf,size) { data=buf; }
+ virtual ~Allocate();
+};
+
+ByteStream::Static::Allocate::~Allocate() {}
+
+inline int
+ByteStream::Static::size(void) const
+{
+ return bsize;
+}
+
+class ByteStream::Static::Duplicate : public ByteStream::Static
+{
+protected:
+ GP<ByteStream> gbs;
+public:
+ Duplicate(const ByteStream::Static &bs, const size_t size);
+};
+
+ByteStream::Static::Duplicate::Duplicate(
+ const ByteStream::Static &bs, const size_t xsize)
+: ByteStream::Static(0,0)
+{
+ if(xsize&&(bs.bsize<bs.where))
+ {
+ const size_t bssize=(size_t)bs.bsize-(size_t)bs.where;
+ bsize=(size_t)((xsize>bssize)?bssize:xsize);
+ gbs=const_cast<ByteStream::Static *>(&bs);
+ data=bs.data+bs.where;
+ }
+}
+
+GP<ByteStream>
+ByteStream::Static::duplicate(const size_t xsize) const
+{
+ return new ByteStream::Static::Duplicate(*this,xsize);
+}
+
+#if HAS_MEMMAP
+/** Read-only ByteStream interface to a memmap area.
+ Class #MemoryMapByteStream# implements a read-only ByteStream interface
+ for a memory map to a file. */
+
+class MemoryMapByteStream : public ByteStream::Static
+{
+public:
+ MemoryMapByteStream(void);
+ virtual ~MemoryMapByteStream();
+private:
+ GUTF8String init(const int fd, const bool closeme);
+ GUTF8String init(FILE *const f,const bool closeme);
+ friend class ByteStream;
+};
+#endif
+
+//// CLASS BYTESTREAM
+
+
+ByteStream::~ByteStream()
+{
+}
+
+int
+ByteStream::scanf(const char *fmt, ...)
+{
+ G_THROW( ERR_MSG("ByteStream.not_implemented") ); // This is a place holder function.
+ return 0;
+}
+
+size_t
+ByteStream::read(void *buffer, size_t sz)
+{
+ G_THROW( ERR_MSG("ByteStream.cant_read") ); // Cannot read from a ByteStream created for writing
+ return 0;
+}
+
+size_t
+ByteStream::write(const void *buffer, size_t sz)
+{
+ G_THROW( ERR_MSG("ByteStream.cant_write") ); // Cannot write from a ByteStream created for reading
+ return 0;
+}
+
+void
+ByteStream::flush()
+{
+}
+
+int
+ByteStream::seek(long offset, int whence, bool nothrow)
+{
+ int nwhere = 0;
+ int ncurrent = tell();
+ switch (whence)
+ {
+ case SEEK_SET:
+ nwhere=0; break;
+ case SEEK_CUR:
+ nwhere=ncurrent; break;
+ case SEEK_END:
+ {
+ if(offset)
+ {
+ if (nothrow)
+ return -1;
+ G_THROW( ERR_MSG("ByteStream.backward") );
+ }
+ char buffer[1024];
+ int bytes;
+ while((bytes=read(buffer, sizeof(buffer))))
+ EMPTY_LOOP;
+ return 0;
+ }
+ default:
+ G_THROW( ERR_MSG("ByteStream.bad_arg") ); // Illegal argument in seek
+ }
+ nwhere += offset;
+ if (nwhere < ncurrent)
+ {
+ // Seeking backwards is not supported by this ByteStream
+ if (nothrow)
+ return -1;
+ G_THROW( ERR_MSG("ByteStream.backward") );
+ }
+ while (nwhere>ncurrent)
+ {
+ char buffer[1024];
+ const int xbytes=(ncurrent+(int)sizeof(buffer)>nwhere)
+ ?(nwhere - ncurrent):(int)sizeof(buffer);
+ const int bytes = read(buffer, xbytes);
+ ncurrent += bytes;
+ if (!bytes)
+ G_THROW( ByteStream::EndOfFile );
+ // Seeking works funny on this ByteStream (ftell() acts strange)
+ if (ncurrent!=tell())
+ G_THROW( ERR_MSG("ByteStream.seek") );
+ }
+ return 0;
+}
+
+size_t
+ByteStream::readall(void *buffer, size_t size)
+{
+ size_t total = 0;
+ while (size > 0)
+ {
+ int nitems = read(buffer, size);
+ // Replaced perror() below with G_THROW(). It still makes little sense
+ // as there is no guarantee, that errno is right. Still, throwing
+ // exception instead of continuing to loop is better.
+ // - eaf
+ if(nitems < 0)
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+ if (nitems == 0)
+ break;
+ total += nitems;
+ size -= nitems;
+ buffer = (void*)((char*)buffer + nitems);
+ }
+ return total;
+}
+
+size_t
+ByteStream::format(const char *fmt, ... )
+{
+ va_list args;
+ va_start(args, fmt);
+ const GUTF8String message(fmt,args);
+ return writestring(message);
+}
+
+size_t
+ByteStream::writestring(const GNativeString &s)
+{
+ int retval;
+ if(cp != UTF8)
+ {
+ retval=writall((const char *)s,s.length());
+ if(cp == AUTO)
+ cp=NATIVE; // Avoid mixing string types.
+ }else
+ {
+ const GUTF8String msg(s.getNative2UTF8());
+ retval=writall((const char *)msg,msg.length());
+ }
+ return retval;
+}
+
+size_t
+ByteStream::writestring(const GUTF8String &s)
+{
+ int retval;
+ if(cp != NATIVE)
+ {
+ retval=writall((const char *)s,s.length());
+ if(cp == AUTO)
+ cp=UTF8; // Avoid mixing string types.
+ }else
+ {
+ const GNativeString msg(s.getUTF82Native());
+ retval=writall((const char *)msg,msg.length());
+ }
+ return retval;
+}
+
+size_t
+ByteStream::writall(const void *buffer, size_t size)
+{
+ size_t total = 0;
+ while (size > 0)
+ {
+ size_t nitems = write(buffer, size);
+ if (nitems == 0)
+ G_THROW( ERR_MSG("ByteStream.write_error") ); // Unknown error in write
+ total += nitems;
+ size -= nitems;
+ buffer = (void*)((char*)buffer + nitems);
+ }
+ return total;
+}
+
+size_t
+ByteStream::copy(ByteStream &bsfrom, size_t size)
+{
+ size_t total = 0;
+ const size_t max_buffer_size=200*1024;
+ const size_t buffer_size=(size>0 && size<max_buffer_size)
+ ?size:max_buffer_size;
+ char *buffer;
+ GPBuffer<char> gbuf(buffer,buffer_size);
+ for(;;)
+ {
+ size_t bytes = buffer_size;
+ if (size>0 && bytes+total>size)
+ bytes = size - total;
+ if (bytes == 0)
+ break;
+ bytes = bsfrom.read((void*)buffer, bytes);
+ if (bytes == 0)
+ break;
+ writall((void*)buffer, bytes);
+ total += bytes;
+ }
+ return total;
+}
+
+
+void
+ByteStream::write8 (unsigned int card)
+{
+ unsigned char c[1];
+ c[0] = (card) & 0xff;
+ if (write((void*)c, sizeof(c)) != sizeof(c))
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+}
+
+void
+ByteStream::write16(unsigned int card)
+{
+ unsigned char c[2];
+ c[0] = (card>>8) & 0xff;
+ c[1] = (card) & 0xff;
+ if (writall((void*)c, sizeof(c)) != sizeof(c))
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+}
+
+void
+ByteStream::write24(unsigned int card)
+{
+ unsigned char c[3];
+ c[0] = (card>>16) & 0xff;
+ c[1] = (card>>8) & 0xff;
+ c[2] = (card) & 0xff;
+ if (writall((void*)c, sizeof(c)) != sizeof(c))
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+}
+
+void
+ByteStream::write32(unsigned int card)
+{
+ unsigned char c[4];
+ c[0] = (card>>24) & 0xff;
+ c[1] = (card>>16) & 0xff;
+ c[2] = (card>>8) & 0xff;
+ c[3] = (card) & 0xff;
+ if (writall((void*)c, sizeof(c)) != sizeof(c))
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+}
+
+unsigned int
+ByteStream::read8 ()
+{
+ unsigned char c[1];
+ if (readall((void*)c, sizeof(c)) != sizeof(c))
+ G_THROW( ByteStream::EndOfFile );
+ return c[0];
+}
+
+unsigned int
+ByteStream::read16()
+{
+ unsigned char c[2];
+ if (readall((void*)c, sizeof(c)) != sizeof(c))
+ G_THROW( ByteStream::EndOfFile );
+ return (c[0]<<8)+c[1];
+}
+
+unsigned int
+ByteStream::read24()
+{
+ unsigned char c[3];
+ if (readall((void*)c, sizeof(c)) != sizeof(c))
+ G_THROW( ByteStream::EndOfFile );
+ return (((c[0]<<8)+c[1])<<8)+c[2];
+}
+
+unsigned int
+ByteStream::read32()
+{
+ unsigned char c[4];
+ if (readall((void*)c, sizeof(c)) != sizeof(c))
+ G_THROW( ByteStream::EndOfFile );
+ return (((((c[0]<<8)+c[1])<<8)+c[2])<<8)+c[3];
+}
+
+
+
+//// CLASS ByteStream::Stdio
+
+ByteStream::Stdio::Stdio(void)
+: can_read(false),can_write(false),must_close(true),fp(0),pos(0)
+{}
+
+ByteStream::Stdio::~Stdio()
+{
+ if (fp && must_close)
+ fclose(fp);
+}
+
+GUTF8String
+ByteStream::Stdio::init(const char mode[])
+{
+ char const *mesg=0;
+ bool binary=false;
+ if(!fp)
+ must_close=false;
+ for (const char *s=mode; s && *s; s++)
+ {
+ switch(*s)
+ {
+ case 'r':
+ can_read=true;
+ if(!fp) fp=stdin;
+ break;
+ case 'w':
+ case 'a':
+ can_write=true;
+ if(!fp) fp=stdout;
+ break;
+ case '+':
+ can_read=can_write=true;
+ break;
+ case 'b':
+ binary=true;
+ break;
+ default:
+ mesg= ERR_MSG("ByteStream.bad_mode"); // Illegal mode in Stdio
+ }
+ }
+ if(binary && fp) {
+#if defined(__CYGWIN32__)
+ setmode(fileno(fp), O_BINARY);
+#elif defined(WIN32)
+ _setmode(_fileno(fp), _O_BINARY);
+#endif
+ }
+ GUTF8String retval;
+ if(!mesg)
+ {
+ tell();
+ }else
+ {
+ retval=mesg;
+ }
+ if(mesg &&(fp && must_close))
+ {
+ fclose(fp);
+ fp=0;
+ must_close=false;
+ }
+ return retval;
+}
+
+static FILE *
+urlfopen(const GURL &url,const char mode[])
+{
+#ifdef WIN32
+ FILE *retval=0;
+ const GUTF8String filename(url.UTF8Filename());
+ wchar_t *wfilename;
+ const size_t wfilename_size=filename.length()+1;
+ GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size);
+ if(filename.ncopy(wfilename,wfilename_size) > 0)
+ {
+ const GUTF8String gmode(mode);
+ wchar_t *wmode;
+ const size_t wmode_size=gmode.length()+1;
+ GPBuffer<wchar_t> gwmode(wmode,wmode_size);
+ if(gmode.ncopy(wmode,wmode_size) > 0)
+ {
+ retval=_wfopen(wfilename,wmode);
+ }
+ }
+ return retval?retval:fopen((const char *)url.NativeFilename(),mode);
+#else
+ return fopen((const char *)url.NativeFilename(),mode);
+#endif
+}
+
+#ifdef UNIX
+static int
+urlopen(const GURL &url, const int mode, const int perm)
+{
+ return open((const char *)url.NativeFilename(),mode,perm);
+}
+#endif /* UNIX */
+
+GUTF8String
+ByteStream::Stdio::init(const GURL &url, const char mode[])
+{
+ GUTF8String retval;
+ if (url.fname() != "-")
+ {
+ fp = urlfopen(url,mode);
+ if (!fp)
+ {
+ // Failed to open '%s': %s
+ G_THROW( ERR_MSG("ByteStream.open_fail") "\t" + url.name()
+ +"\t"+GNativeString(strerror(errno)).getNative2UTF8());
+ }
+ }
+ return retval.length()?retval:init(mode);
+}
+
+size_t
+ByteStream::Stdio::read(void *buffer, size_t size)
+{
+ if (!can_read)
+ G_THROW( ERR_MSG("ByteStream.no_read") ); // Stdio not opened for reading
+ size_t nitems;
+ do
+ {
+ clearerr(fp);
+ nitems = fread(buffer, 1, size, fp);
+ if (nitems<=0 && ferror(fp))
+ {
+#ifdef EINTR
+ if (errno!=EINTR)
+#endif
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+ }
+ else
+ break;
+ } while(true);
+ pos += nitems;
+ return nitems;
+}
+
+size_t
+ByteStream::Stdio::write(const void *buffer, size_t size)
+{
+ if (!can_write)
+ G_THROW( ERR_MSG("ByteStream.no_write") ); // Stdio not opened for writing
+ size_t nitems;
+ do
+ {
+ clearerr(fp);
+ nitems = fwrite(buffer, 1, size, fp);
+ if (nitems<=0 && ferror(fp))
+ {
+#ifdef EINTR
+ if (errno!=EINTR)
+#endif
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+ }
+ else
+ break;
+ } while(true);
+ pos += nitems;
+ return nitems;
+}
+
+void
+ByteStream::Stdio::flush()
+{
+ if (fflush(fp) < 0)
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+}
+
+long
+ByteStream::Stdio::tell(void) const
+{
+ long x = ftell(fp);
+ if (x >= 0)
+ {
+ Stdio *sbs=const_cast<Stdio *>(this);
+ (sbs->pos) = x;
+ }else
+ {
+ x=pos;
+ }
+ return x;
+}
+
+int
+ByteStream::Stdio::seek(long offset, int whence, bool nothrow)
+{
+ if (whence==SEEK_SET && offset>=0 && offset==ftell(fp))
+ return 0;
+ clearerr(fp);
+ if (fseek(fp, offset, whence))
+ {
+ if (nothrow)
+ return -1;
+ G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
+ }
+ return tell();
+}
+
+
+
+
+///////// ByteStream::Memory
+
+ByteStream::Memory::Memory()
+ : where(0), bsize(0), nblocks(0), gblocks(blocks,0)
+{
+}
+
+GUTF8String
+ByteStream::Memory::init(void const * const buffer, const size_t sz)
+{
+ GUTF8String retval;
+ G_TRY
+ {
+ writall(buffer, sz);
+ where = 0;
+ }
+ G_CATCH(ex) // The only error that should be thrown is out of memory...
+ {
+ retval=ex.get_cause();
+ }
+ G_ENDCATCH;
+ return retval;
+}
+
+void
+ByteStream::Memory::empty()
+{
+ for (int b=0; b<nblocks; b++)
+ {
+ delete [] blocks[b];
+ blocks[b]=0;
+ }
+ bsize = 0;
+ where = 0;
+ nblocks = 0;
+}
+
+ByteStream::Memory::~Memory()
+{
+ empty();
+}
+
+size_t
+ByteStream::Memory::write(const void *buffer, size_t sz)
+{
+ int nsz = (int)sz;
+ if (nsz <= 0)
+ return 0;
+ // check memory
+ if ( (where+nsz) > ((bsize+0xfff)&~0xfff) )
+ {
+ // reallocate pointer array
+ if ( (where+nsz) > (nblocks<<12) )
+ {
+ const int old_nblocks=nblocks;
+ nblocks = (((where+nsz)+0xffff)&~0xffff) >> 12;
+ gblocks.resize(nblocks);
+ char const ** eblocks=(char const **)(blocks+old_nblocks);
+ for(char const * const * const new_eblocks=blocks+nblocks;
+ eblocks <new_eblocks; eblocks++)
+ {
+ *eblocks = 0;
+ }
+ }
+ // allocate blocks
+ for (int b=(where>>12); (b<<12)<(where+nsz); b++)
+ {
+ if (! blocks[b])
+ blocks[b] = new char[0x1000];
+ }
+ }
+ // write data to buffer
+ while (nsz > 0)
+ {
+ int n = (where|0xfff) + 1 - where;
+ n = ((nsz < n) ? nsz : n);
+ memcpy( (void*)&blocks[where>>12][where&0xfff], buffer, n);
+ buffer = (void*) ((char*)buffer + n);
+ where += n;
+ nsz -= n;
+ }
+ // adjust size
+ if (where > bsize)
+ bsize = where;
+ return sz;
+}
+
+size_t
+ByteStream::Memory::readat(void *buffer, size_t sz, int pos)
+{
+ if ((int) sz > bsize - pos)
+ sz = bsize - pos;
+ int nsz = (int)sz;
+ if (nsz <= 0)
+ return 0;
+ // read data from buffer
+ while (nsz > 0)
+ {
+ int n = (pos|0xfff) + 1 - pos;
+ n = ((nsz < n) ? nsz : n);
+ memcpy(buffer, (void*)&blocks[pos>>12][pos&0xfff], n);
+ buffer = (void*) ((char*)buffer + n);
+ pos += n;
+ nsz -= n;
+ }
+ return sz;
+}
+
+size_t
+ByteStream::Memory::read(void *buffer, size_t sz)
+{
+ sz = readat(buffer,sz,where);
+ where += sz;
+ return sz;
+}
+
+long
+ByteStream::Memory::tell(void) const
+{
+ return where;
+}
+
+int
+ByteStream::Memory::seek(long offset, int whence, bool nothrow)
+{
+ int nwhere = 0;
+ switch (whence)
+ {
+ case SEEK_SET: nwhere = 0; break;
+ case SEEK_CUR: nwhere = where; break;
+ case SEEK_END: nwhere = bsize; break;
+ default: G_THROW( ERR_MSG("bad_arg") "\tByteStream::Memory::seek()"); // Illegal argument in ByteStream::Memory::seek()
+ }
+ nwhere += offset;
+ if (nwhere<0)
+ G_THROW( ERR_MSG("ByteStream.seek_error2") ); // Attempt to seek before the beginning of the file
+ where = nwhere;
+ return 0;
+}
+
+
+
+/** This function has been moved into Arrays.cpp
+ In order to avoid dependencies from ByteStream.o
+ to Arrays.o */
+#ifdef DO_NOT_MOVE_GET_DATA_TO_ARRAYS_CPP
+TArray<char>
+ByteStream::get_data(void)
+{
+ TArray<char> data(0, size()-1);
+ readat((char*)data, size(), 0);
+ return data;
+}
+#endif
+
+
+///////// ByteStream::Static
+
+ByteStream::Static::Static(const void * const buffer, const size_t sz)
+ : data((const char *)buffer), bsize(sz), where(0)
+{
+}
+
+size_t
+ByteStream::Static::read(void *buffer, size_t sz)
+{
+ int nsz = (int)sz;
+ if (nsz > bsize - where)
+ nsz = bsize - where;
+ if (nsz <= 0)
+ return 0;
+ memcpy(buffer, data+where, nsz);
+ where += nsz;
+ return nsz;
+}
+
+int
+ByteStream::Static::seek(long offset, int whence, bool nothrow)
+{
+ int nwhere = 0;
+ switch (whence)
+ {
+ case SEEK_SET: nwhere = 0; break;
+ case SEEK_CUR: nwhere = where; break;
+ case SEEK_END: nwhere = bsize; break;
+ default: G_THROW("bad_arg\tByteStream::Static::seek()"); // Illegal argument to ByteStream::Static::seek()
+ }
+ nwhere += offset;
+ if (nwhere<0)
+ G_THROW( ERR_MSG("ByteStream.seek_error2") ); // Attempt to seek before the beginning of the file
+ where = nwhere;
+ return 0;
+}
+
+long
+ByteStream::Static::tell(void) const
+{
+ return where;
+}
+
+GP<ByteStream>
+ByteStream::create(void)
+{
+ return new Memory();
+}
+
+GP<ByteStream>
+ByteStream::create(void const * const buffer, const size_t size)
+{
+ Memory *mbs=new Memory();
+ GP<ByteStream> retval=mbs;
+ mbs->init(buffer,size);
+ return retval;
+}
+
+GP<ByteStream>
+ByteStream::create(const GURL &url,char const * const xmode)
+{
+ GP<ByteStream> retval;
+ const char *mode = ((xmode) ? xmode : "rb");
+#ifdef UNIX
+ if (!strcmp(mode,"rb"))
+ {
+ int fd = urlopen(url,O_RDONLY,0777);
+ if (fd >= 0)
+ {
+#if HAS_MEMMAP && defined(S_IFREG)
+ struct stat buf;
+ if ( (fstat(fd, &buf) >= 0) && (buf.st_mode & S_IFREG) )
+ {
+ MemoryMapByteStream *rb = new MemoryMapByteStream();
+ retval = rb;
+ GUTF8String errmessage = rb->init(fd,true);
+ if(errmessage.length())
+ retval=0;
+ }
+#endif
+ if (! retval)
+ {
+ FILE *f = fdopen(fd, mode);
+ if (f)
+ {
+ Stdio *sbs=new Stdio();
+ retval=sbs;
+ GUTF8String errmessage=sbs->init(f, mode, true);
+ if(errmessage.length())
+ retval=0;
+ }
+ }
+ if (! retval)
+ close(fd);
+ }
+ }
+#endif
+ if (! retval)
+ {
+ Stdio *sbs=new Stdio();
+ retval=sbs;
+ GUTF8String errmessage=sbs->init(url, mode);
+ if(errmessage.length())
+ G_THROW(errmessage);
+ }
+ return retval;
+}
+
+GP<ByteStream>
+ByteStream::create(char const * const mode)
+{
+ GP<ByteStream> retval;
+ Stdio *sbs=new Stdio();
+ retval=sbs;
+ GUTF8String errmessage=sbs->init(mode?mode:"rb");
+ if(errmessage.length())
+ {
+ G_THROW(errmessage);
+ }
+ return retval;
+}
+
+GP<ByteStream>
+ByteStream::create(const int fd,char const * const mode,const bool closeme)
+{
+ GP<ByteStream> retval;
+ const char *default_mode="rb";
+#if HAS_MEMMAP
+ if ( (!mode&&(fd!=0)&&(fd!=1)&&(fd!=2))
+ || (mode&&(GUTF8String("rb") == mode)))
+ {
+ MemoryMapByteStream *rb=new MemoryMapByteStream();
+ retval=rb;
+ GUTF8String errmessage=rb->init(fd,closeme);
+ if(errmessage.length())
+ {
+ retval=0;
+ }
+ }
+ if(!retval)
+#endif
+ {
+ int fd2 = fd;
+ FILE *f = 0;
+ if (fd == 0 && !closeme
+ && (!mode || mode[0]=='r') )
+ {
+ f=stdin;
+ default_mode = "r";
+ fd2=(-1);
+ }
+ else if (fd == 1 && !closeme
+ && (!mode || mode[0]=='a' || mode[0]=='w') )
+ {
+ default_mode = "a";
+ f=stdout;
+ fd2 = -1;
+ }
+ else if (fd == 2 && !closeme
+ && (!mode || mode[0]=='a' || mode[0]=='w') )
+ {
+ default_mode = "a";
+ f=stderr;
+ fd2 = -1;
+ }
+ else
+ {
+ if (! closeme)
+ fd2 = dup(fd);
+ f = fdopen(fd2,(char*)(mode?mode:default_mode));
+ }
+
+ if(!f)
+ {
+ if ( fd2 >= 0)
+ close(fd2);
+ G_THROW( ERR_MSG("ByteStream.open_fail2") );
+ }
+ Stdio *sbs=new Stdio();
+ retval=sbs;
+ GUTF8String errmessage=sbs->init(f,mode?mode:default_mode,(fd2>=0));
+ if(errmessage.length())
+ G_THROW(errmessage);
+ }
+ return retval;
+}
+
+GP<ByteStream>
+ByteStream::create(FILE * const f,char const * const mode,const bool closeme)
+{
+ GP<ByteStream> retval;
+#if HAS_MEMMAP
+ if (!mode || (GUTF8String("rb") == mode))
+ {
+ MemoryMapByteStream *rb=new MemoryMapByteStream();
+ retval=rb;
+ GUTF8String errmessage=rb->init(fileno(f),false);
+ if(errmessage.length())
+ {
+ retval=0;
+ }else
+ {
+ fclose(f);
+ }
+ }
+ if(!retval)
+#endif
+ {
+ Stdio *sbs=new Stdio();
+ retval=sbs;
+ GUTF8String errmessage=sbs->init(f,mode?mode:"rb",closeme);
+ if(errmessage.length())
+ {
+ G_THROW(errmessage);
+ }
+ }
+ return retval;
+}
+
+GP<ByteStream>
+ByteStream::create_static(const void * const buffer, size_t sz)
+{
+ return new Static(buffer, sz);
+}
+
+GP<ByteStream>
+ByteStream::duplicate(const size_t xsize) const
+{
+ GP<ByteStream> retval;
+ const long int pos=tell();
+ const int tsize=size();
+ ByteStream &self=*(const_cast<ByteStream *>(this));
+ if(tsize < 0 || pos < 0 || (unsigned int)tsize < 1+(unsigned int)pos)
+ {
+ retval=ByteStream::create();
+ retval->copy(self,xsize);
+ retval->seek(0L);
+ }else
+ {
+ const size_t s=(size_t)tsize-(size_t)pos;
+ const int size=(!xsize||(s<xsize))?s:xsize;
+ ByteStream::Static::Allocate *bs=new ByteStream::Static::Allocate(size);
+ retval=bs;
+ self.readall(bs->buf,size);
+ }
+ self.seek(pos,SEEK_SET,true);
+ return retval;
+}
+
+
+#if HAS_MEMMAP
+MemoryMapByteStream::MemoryMapByteStream(void)
+: ByteStream::Static(0,0)
+{}
+
+GUTF8String
+MemoryMapByteStream::init(FILE *const f,const bool closeme)
+{
+ GUTF8String retval;
+ retval=init(fileno(f),false);
+ if(closeme)
+ {
+ fclose(f);
+ }
+ return retval;
+}
+
+GUTF8String
+MemoryMapByteStream::init(const int fd,const bool closeme)
+{
+ GUTF8String retval;
+#if defined(PROT_READ) && defined(MAP_SHARED)
+ struct stat statbuf;
+ if(!fstat(fd,&statbuf))
+ {
+ if(statbuf.st_size)
+ {
+ bsize=statbuf.st_size;
+ data=(char *)mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fd,0);
+ }
+ }else
+ {
+ if(closeme)
+ {
+ close(fd);
+ }
+ retval= ERR_MSG("ByteStream.open_fail2");
+ }
+#else
+ retval= ERR_MSG("ByteStream.open_fail2");
+#endif
+ if(closeme)
+ {
+ close(fd);
+ }
+ return retval;
+}
+
+MemoryMapByteStream::~MemoryMapByteStream()
+{
+ if(data)
+ {
+ munmap(const_cast<char *>(data),bsize);
+ }
+}
+
+#endif
+
+ByteStream::Wrapper::~Wrapper() {}
+
+
+GP<ByteStream>
+ByteStream::get_stdin(char const * const mode)
+{
+ static GP<ByteStream> gp = ByteStream::create(0,mode,false);
+ return gp;
+}
+
+GP<ByteStream>
+ByteStream::get_stdout(char const * const mode)
+{
+ static GP<ByteStream> gp = ByteStream::create(1,mode,false);
+ return gp;
+}
+
+GP<ByteStream>
+ByteStream::get_stderr(char const * const mode)
+{
+ static GP<ByteStream> gp = ByteStream::create(2,mode,false);
+ return gp;
+}
+
+
+/** Looks up the message and writes it to the specified stream. */
+void ByteStream::formatmessage( const char *fmt, ... )
+{
+ va_list args;
+ va_start(args, fmt);
+ const GUTF8String message(fmt,args);
+ writemessage( message );
+}
+
+/** Looks up the message and writes it to the specified stream. */
+void ByteStream::writemessage( const char *message )
+{
+ writestring( DjVuMessage::LookUpUTF8( message ) );
+}
+
+static void
+read_file(ByteStream &bs,char *&buffer,GPBuffer<char> &gbuffer)
+{
+ const int size=bs.size();
+ int pos=0;
+ if(size>0)
+ {
+ size_t readsize=size+1;
+ gbuffer.resize(readsize);
+ for(int i;readsize&&(i=bs.read(buffer+pos,readsize))>0;pos+=i,readsize-=i)
+ EMPTY_LOOP;
+ }else
+ {
+ const size_t readsize=32768;
+ gbuffer.resize(readsize);
+ for(int i;((i=bs.read(buffer+pos,readsize))>0);
+ gbuffer.resize((pos+=i)+readsize))
+ EMPTY_LOOP;
+ }
+ buffer[pos]=0;
+}
+
+GNativeString
+ByteStream::getAsNative(void)
+{
+ char *buffer;
+ GPBuffer<char> gbuffer(buffer);
+ read_file(*this,buffer,gbuffer);
+ return GNativeString(buffer);
+}
+
+GUTF8String
+ByteStream::getAsUTF8(void)
+{
+ char *buffer;
+ GPBuffer<char> gbuffer(buffer);
+ read_file(*this,buffer,gbuffer);
+ return GUTF8String(buffer);
+}
+
+
+#ifdef HAVE_NAMESPACES
+}
+# ifndef NOT_USING_DJVU_NAMESPACE
+using namespace DJVU;
+# endif
+#endif
+
+void
+DjVuPrintErrorUTF8(const char *fmt, ... )
+{
+ G_TRY {
+ GP<ByteStream> errout = ByteStream::get_stderr();
+ if (errout)
+ {
+ errout->cp=ByteStream::NATIVE;
+ va_list args;
+ va_start(args, fmt);
+ const GUTF8String message(fmt,args);
+ errout->writestring(message);
+ }
+ // Need to catch all exceptions because these might be
+ // called from an outer exception handler (with prejudice)
+ } G_CATCH_ALL { } G_ENDCATCH;
+}
+
+void
+DjVuPrintErrorNative(const char *fmt, ... )
+{
+ G_TRY {
+ GP<ByteStream> errout = ByteStream::get_stderr();
+ if (errout)
+ {
+ errout->cp=ByteStream::NATIVE;
+ va_list args;
+ va_start(args, fmt);
+ const GNativeString message(fmt,args);
+ errout->writestring(message);
+ }
+ // Need to catch all exceptions because these might be
+ // called from an outer exception handler (with prejudice)
+ } G_CATCH_ALL { } G_ENDCATCH;
+}
+
+void
+DjVuPrintMessageUTF8(const char *fmt, ... )
+{
+ G_TRY {
+ GP<ByteStream> strout = ByteStream::get_stdout();
+ if (strout)
+ {
+ strout->cp=ByteStream::NATIVE;
+ va_list args;
+ va_start(args, fmt);
+ const GUTF8String message(fmt,args);
+ strout->writestring(message);
+ }
+ // Need to catch all exceptions because these might be
+ // called from an outer exception handler (with prejudice)
+ } G_CATCH_ALL { } G_ENDCATCH;
+}
+
+void
+DjVuPrintMessageNative(const char *fmt, ... )
+{
+ G_TRY {
+ GP<ByteStream> strout = ByteStream::get_stdout();
+ if (strout)
+ {
+ strout->cp=ByteStream::NATIVE;
+ va_list args;
+ va_start(args, fmt);
+ const GNativeString message(fmt,args);
+ strout->writestring(message);
+ }
+ // Need to catch all exceptions because these might be
+ // called from an outer exception handler (with prejudice)
+ } G_CATCH_ALL { } G_ENDCATCH;
+}