//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. // . #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 #if defined(WIN32) || defined(__CYGWIN32__) # include #endif #ifdef UNIX # ifndef HAS_MEMMAP # define HAS_MEMMAP 1 # endif #endif #if defined(UNIX) # include # include # include # include # ifdef HAS_MEMMAP # include # endif #elif defined(macintosh) # include _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 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 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 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 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.bsizebssize)?bssize:xsize); gbs=const_cast(&bs); data=bs.data+bs.where; } } GP 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 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 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 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(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 ((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 >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 ByteStream::get_data(void) { TArray 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::create(void) { return new Memory(); } GP ByteStream::create(void const * const buffer, const size_t size) { Memory *mbs=new Memory(); GP retval=mbs; mbs->init(buffer,size); return retval; } GP ByteStream::create(const GURL &url,char const * const xmode) { GP 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::create(char const * const mode) { GP retval; Stdio *sbs=new Stdio(); retval=sbs; GUTF8String errmessage=sbs->init(mode?mode:"rb"); if(errmessage.length()) { G_THROW(errmessage); } return retval; } GP ByteStream::create(const int fd,char const * const mode,const bool closeme) { GP 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::create(FILE * const f,char const * const mode,const bool closeme) { GP 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::create_static(const void * const buffer, size_t sz) { return new Static(buffer, sz); } GP ByteStream::duplicate(const size_t xsize) const { GP retval; const long int pos=tell(); const int tsize=size(); ByteStream &self=*(const_cast(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||(sbuf,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(data),bsize); } } #endif ByteStream::Wrapper::~Wrapper() {} GP ByteStream::get_stdin(char const * const mode) { static GP gp = ByteStream::create(0,mode,false); return gp; } GP ByteStream::get_stdout(char const * const mode) { static GP gp = ByteStream::create(1,mode,false); return gp; } GP ByteStream::get_stderr(char const * const mode) { static GP 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 &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 gbuffer(buffer); read_file(*this,buffer,gbuffer); return GNativeString(buffer); } GUTF8String ByteStream::getAsUTF8(void) { char *buffer; GPBuffer 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 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 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 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 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; }