1 //C-  -*- C++ -*-
2 //C- -------------------------------------------------------------------
3 //C- DjVuLibre-3.5
4 //C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5 //C- Copyright (c) 2001  AT&T
6 //C-
7 //C- This software is subject to, and may be distributed under, the
8 //C- GNU General Public License, either Version 2 of the license,
9 //C- or (at your option) any later version. The license should have
10 //C- accompanied the software or you may obtain a copy of the license
11 //C- from the Free Software Foundation at http://www.fsf.org .
12 //C-
13 //C- This program is distributed in the hope that it will be useful,
14 //C- but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //C- GNU General Public License for more details.
17 //C-
18 //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
19 //C- Lizardtech Software.  Lizardtech Software has authorized us to
20 //C- replace the original DjVu(r) Reference Library notice by the following
21 //C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
22 //C-
23 //C-  ------------------------------------------------------------------
24 //C- | DjVu (r) Reference Library (v. 3.5)
25 //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
26 //C- | The DjVu Reference Library is protected by U.S. Pat. No.
27 //C- | 6,058,214 and patents pending.
28 //C- |
29 //C- | This software is subject to, and may be distributed under, the
30 //C- | GNU General Public License, either Version 2 of the license,
31 //C- | or (at your option) any later version. The license should have
32 //C- | accompanied the software or you may obtain a copy of the license
33 //C- | from the Free Software Foundation at http://www.fsf.org .
34 //C- |
35 //C- | The computer code originally released by LizardTech under this
36 //C- | license and unmodified by other parties is deemed "the LIZARDTECH
37 //C- | ORIGINAL CODE."  Subject to any third party intellectual property
38 //C- | claims, LizardTech grants recipient a worldwide, royalty-free,
39 //C- | non-exclusive license to make, use, sell, or otherwise dispose of
40 //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
41 //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
42 //C- | General Public License.   This grant only confers the right to
43 //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
44 //C- | the extent such infringement is reasonably necessary to enable
45 //C- | recipient to make, have made, practice, sell, or otherwise dispose
46 //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
47 //C- | any greater extent that may be necessary to utilize further
48 //C- | modifications or combinations.
49 //C- |
50 //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
51 //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
52 //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
53 //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
54 //C- +------------------------------------------------------------------
55 
56 #ifdef HAVE_CONFIG_H
57 # include "config.h"
58 #endif
59 #if NEED_GNUG_PRAGMAS
60 # pragma implementation
61 #endif
62 
63 // -- Implementation of IFFByteStream
64 // - Author: Leon Bottou, 06/1998
65 
66 // From: Leon Bottou, 1/31/2002
67 // This has been changed by Lizardtech to fit better
68 // with their re-implementation of ByteStreams.
69 
70 #include <assert.h>
71 #include "IFFByteStream.h"
72 
73 
74 #ifdef HAVE_NAMESPACES
75 namespace DJVU {
76 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
77 }
78 #endif
79 #endif
80 
81 
82 // Constructor
IFFByteStream(const GP<ByteStream> & xbs,const int xpos)83 IFFByteStream::IFFByteStream(const GP<ByteStream> &xbs,const int xpos)
84   : ByteStream::Wrapper(xbs), ctx(0), dir(0)
85 {
86   offset = seekto = xpos;
87   has_magic_att = false;
88   has_magic_sdjv = false;
89 }
90 
91 // Destructor
~IFFByteStream()92 IFFByteStream::~IFFByteStream()
93 {
94   while (ctx)
95     close_chunk();
96 }
97 
98 GP<IFFByteStream>
create(const GP<ByteStream> & bs)99 IFFByteStream::create(const GP<ByteStream> &bs)
100 {
101   const int pos=bs->tell();
102   return new IFFByteStream(bs,pos);
103 }
104 
105 
106 // IFFByteStream::ready
107 // -- indicates if bytestream is ready for reading
108 //    returns number of bytes available
109 
110 int
ready()111 IFFByteStream::ready()
112 {
113   if (ctx && dir < 0)
114     return ctx->offEnd - offset;
115   else if (ctx)
116     return 1;
117   else
118     return 0;
119 }
120 
121 
122 // IFFByteStream::composite
123 // -- indicates if bytestream is ready for putting or getting chunks
124 
125 int
composite()126 IFFByteStream::composite()
127 {
128   if (ctx && !ctx->bComposite)
129     return 0;
130   else
131     return 1;
132 }
133 
134 
135 
136 
137 // IFFByteStream::check_id
138 // -- checks if an id is legal
139 
140 int
check_id(const char * id)141 IFFByteStream::check_id(const char *id)
142 {
143   int i;
144   // check absence of null bytes
145   for (i=0; i<4; i++)
146     if (id[i]<0x20 || id[i]>0x7e)
147       return -1;
148   // check composite chunks
149   static const char *szComposite[] = { "FORM", "LIST", "PROP", "CAT ", 0 };
150   for (i=0; szComposite[i]; i++)
151     if (!memcmp(id, szComposite[i], 4))
152       return 1;
153   // check reserved chunks
154   static const char *szReserved[] = { "FOR", "LIS", "CAT", 0 };
155   for (i=0; szReserved[i]; i++)
156     if (!memcmp(id, szReserved[i], 3) && id[3]>='1' && id[3]<='9')
157       return -1;
158   // regular chunk
159   return 0;
160 }
161 
162 
163 
164 // IFFByteStream::get_chunk
165 // -- get next chunk header
166 
167 int
get_chunk(GUTF8String & chkid,int * rawoffsetptr,int * rawsizeptr)168 IFFByteStream::get_chunk(GUTF8String &chkid, int *rawoffsetptr, int *rawsizeptr)
169 {
170   int bytes;
171   char buffer[8];
172 
173   // Check that we are allowed to read a chunk
174   if (dir > 0)
175     G_THROW( ERR_MSG("IFFByteStream.read_write") );
176   if (ctx && !ctx->bComposite)
177     G_THROW( ERR_MSG("IFFByteStream.not_ready") );
178   dir = -1;
179 
180   // Seek to end of previous chunk if necessary
181   if (seekto > offset)
182     {
183       bs->seek(seekto);
184       offset = seekto;
185     }
186 
187   // Skip padding byte
188   if (ctx && offset == ctx->offEnd)
189     return 0;
190   if (offset & 1)
191     {
192       bytes = bs->read( (void*)buffer, 1);
193       if (bytes==0 && !ctx)
194         return 0;
195       offset += bytes;
196     }
197 
198   // Record raw offset
199   int rawoffset = offset;
200 
201   // Read chunk id (skipping magic sequences inserted here to make
202   // DjVu files recognizable.)
203   for(;;)
204   {
205     if (ctx && offset == ctx->offEnd)
206       return 0;
207     if (ctx && offset+4 > ctx->offEnd)
208       G_THROW( ERR_MSG("IFFByteStream.corrupt_end") );
209     bytes = bs->readall( (void*)&buffer[0], 4);
210     offset = seekto = offset + bytes;
211     if (bytes==0 && !ctx)
212       return 0;
213     if (bytes != 4)
214       G_THROW(ByteStream::EndOfFile);
215     if (buffer[0] == 'S' && buffer[1] == 'D' &&
216         buffer[2] == 'J' && buffer[3] == 'V' )
217       {
218         has_magic_sdjv = true;
219         continue;
220       }
221     else if (buffer[0] == 'A' && buffer[1] == 'T' &&
222              buffer[2] == '&' && buffer[3] == 'T' )
223       {
224         has_magic_att=true;
225         continue;
226       }
227     else
228       break;
229   }
230 
231   // Read chunk size
232   if (ctx && offset+4 > ctx->offEnd)
233     G_THROW( ERR_MSG("IFFByteStream.corrupt_end2") );
234   bytes = bs->readall( (void*)&buffer[4], 4);
235   offset = seekto = offset + bytes;
236   if (bytes != 4)
237     G_THROW( ByteStream::EndOfFile );
238   long size = ((unsigned char)buffer[4]<<24) |
239               ((unsigned char)buffer[5]<<16) |
240               ((unsigned char)buffer[6]<<8)  |
241               ((unsigned char)buffer[7]);
242   if (ctx && offset+size > ctx->offEnd)
243     G_THROW( ERR_MSG("IFFByteStream.corrupt_mangled") );
244 
245   // Check if composite
246   int composite = check_id(buffer);
247   if (composite < 0)
248     G_THROW( ERR_MSG("IFFByteStream.corrupt_id") );
249 
250   // Read secondary id of composite chunk
251   if (composite)
252   {
253     if (ctx && ctx->offEnd<offset+4)
254       G_THROW( ERR_MSG("IFFByteStream.corrupt_header") );
255     bytes = bs->readall( (void*)&buffer[4], 4);
256     offset += bytes;
257     if (bytes != 4)
258       G_THROW( ByteStream::EndOfFile );
259     if (check_id(&buffer[4]))
260       G_THROW( ERR_MSG("IFFByteStream.corrupt_2nd_id") );
261   }
262 
263   // Create context record
264   IFFContext *nctx = new IFFContext;
265   G_TRY
266   {
267     nctx->next = ctx;
268     nctx->offStart = seekto;
269     nctx->offEnd = seekto + size;
270     if (composite)
271     {
272       memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
273       memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4);
274       nctx->bComposite = 1;
275     }
276     else
277     {
278       memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
279       memset( (void*)(nctx->idTwo), 0, 4);
280       nctx->bComposite = 0;
281     }
282   }
283   G_CATCH_ALL
284   {
285     delete nctx;
286     G_RETHROW;
287   }
288   G_ENDCATCH;
289 
290   // Install context record
291   ctx = nctx;
292   chkid = GUTF8String(ctx->idOne, 4);
293   if (composite)
294     chkid = chkid + ":" + GUTF8String(ctx->idTwo, 4);
295 
296   // Return
297   if (rawoffsetptr)
298     *rawoffsetptr = rawoffset;
299   if (rawsizeptr)
300     *rawsizeptr = ( ctx->offEnd - rawoffset + 1) & ~0x1;
301   return size;
302 }
303 
304 
305 
306 // IFFByteStream::put_chunk
307 // -- write new chunk header
308 
309 void
put_chunk(const char * chkid,int insert_magic)310 IFFByteStream::put_chunk(const char *chkid, int insert_magic)
311 {
312   int bytes;
313   char buffer[8];
314 
315   // Check that we are allowed to write a chunk
316   if (dir < 0)
317     G_THROW( ERR_MSG("IFFByteStream.read_write") );
318   if (ctx && !ctx->bComposite)
319     G_THROW( ERR_MSG("IFFByteStream.not_ready2") );
320   dir = +1;
321 
322   // Check primary id
323   int composite = check_id(chkid);
324   if ((composite<0) || (composite==0 && chkid[4])
325       || (composite && (chkid[4]!=':' || check_id(&chkid[5]) || chkid[9])) )
326     G_THROW( ERR_MSG("IFFByteStream.bad_chunk") );
327 
328   // Write padding byte
329   assert(seekto <= offset);
330   memset((void*)buffer, 0, 8);
331   if (offset & 1)
332     offset += bs->write((void*)&buffer[4], 1);
333 
334   // Insert magic to make this file recognizable as DjVu
335   if (insert_magic)
336   {
337     // Don't change the way you do the file magic!
338     // I rely on these bytes letters in some places
339     // (like DjVmFile.cpp and djvm.cpp) -- eaf
340     buffer[0]=0x41;
341     buffer[1]=0x54;
342     buffer[2]=0x26;
343     buffer[3]=0x54;
344     offset += bs->writall((void*)&buffer[0], 4);
345   }
346 
347   // Write chunk header
348   memcpy((void*)&buffer[0], (void*)&chkid[0], 4);
349   bytes = bs->writall((void*)&buffer[0], 8);
350   offset = seekto = offset + bytes;
351   if (composite)
352   {
353     memcpy((void*)&buffer[4], (void*)&chkid[5], 4);
354     bytes = bs->writall((void*)&buffer[4], 4);
355     offset = offset + bytes;
356   }
357 
358   // Create new context record
359   IFFContext *nctx = new IFFContext;
360   G_TRY
361   {
362     nctx->next = ctx;
363     nctx->offStart = seekto;
364     nctx->offEnd = 0;
365     if (composite)
366     {
367       memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
368       memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4);
369       nctx->bComposite = 1;
370     }
371     else
372     {
373       memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
374       memset( (void*)(nctx->idTwo), 0, 4);
375       nctx->bComposite = 0;
376     }
377   }
378   G_CATCH_ALL
379   {
380     delete nctx;
381     G_RETHROW;
382   }
383   G_ENDCATCH;
384   // Install context record and leave
385   ctx = nctx;
386 }
387 
388 
389 
390 void
close_chunk()391 IFFByteStream::close_chunk()
392 {
393   // Check that this is ok
394   if (!ctx)
395     G_THROW( ERR_MSG("IFFByteStream.cant_close") );
396   // Patch size field in new chunk
397   if (dir > 0)
398     {
399       ctx->offEnd = offset;
400       long size = ctx->offEnd - ctx->offStart;
401       char buffer[4];
402       buffer[0] = (unsigned char)(size>>24);
403       buffer[1] = (unsigned char)(size>>16);
404       buffer[2] = (unsigned char)(size>>8);
405       buffer[3] = (unsigned char)(size);
406       bs->seek(ctx->offStart - 4);
407       bs->writall((void*)buffer, 4);
408       bs->seek(offset);
409     }
410   // Arrange for reader to seek at next chunk
411   seekto = ctx->offEnd;
412   // Remove ctx record
413   IFFContext *octx = ctx;
414   ctx = octx->next;
415   assert(ctx==0 || ctx->bComposite);
416   delete octx;
417 }
418 
419 // This is the same as above, but adds a seek to the close
420 // Otherwise an EOF in this chunk won't be reported until we
421 // try to open the next chunk, which makes error recovery
422 // very difficult.
423 void
seek_close_chunk(void)424 IFFByteStream::seek_close_chunk(void)
425 {
426   close_chunk();
427   if ((dir <= 0)&&((!ctx)||(ctx->bComposite))&&(seekto > offset))
428   {
429     bs->seek(seekto);
430     offset = seekto;
431   }
432 }
433 
434 // IFFByteStream::short_id
435 // Returns the id of the current chunk
436 
437 void
short_id(GUTF8String & chkid)438 IFFByteStream::short_id(GUTF8String &chkid)
439 {
440   if (!ctx)
441     G_THROW( ERR_MSG("IFFByteStream.no_chunk_id") );
442   if (ctx->bComposite)
443     chkid = GUTF8String(ctx->idOne, 4) + ":" + GUTF8String(ctx->idTwo, 4);
444   else
445     chkid = GUTF8String(ctx->idOne, 4);
446 }
447 
448 
449 // IFFByteStream::full_id
450 // Returns the full chunk id of the current chunk
451 
452 void
full_id(GUTF8String & chkid)453 IFFByteStream::full_id(GUTF8String &chkid)
454 {
455   short_id(chkid);
456   if (ctx->bComposite)
457     return;
458   // Search parent FORM or PROP chunk.
459   for (IFFContext *ct = ctx->next; ct; ct=ct->next)
460     if (memcmp(ct->idOne, "FOR", 3)==0 ||
461         memcmp(ct->idOne, "PRO", 3)==0  )
462       {
463         chkid = GUTF8String(ct->idTwo, 4) + "." + chkid;
464         break;
465       }
466 }
467 
468 
469 
470 // IFFByteStream::read
471 // -- read bytes from IFF file chunk
472 
473 size_t
read(void * buffer,size_t size)474 IFFByteStream::read(void *buffer, size_t size)
475 {
476   if (! (ctx && dir < 0))
477     G_THROW( ERR_MSG("IFFByteStream.not_ready3") );
478   // Seek if necessary
479   if (seekto > offset) {
480     bs->seek(seekto);
481     offset = seekto;
482   }
483   // Ensure that read does not extend beyond chunk
484   if (offset > ctx->offEnd)
485     G_THROW( ERR_MSG("IFFByteStream.bad_offset") );
486   if (offset + (long)size >  ctx->offEnd)
487     size = (size_t) (ctx->offEnd - offset);
488   // Read bytes
489   size_t bytes = bs->read(buffer, size);
490   offset += bytes;
491   return bytes;
492 }
493 
494 
495 // IFFByteStream::write
496 // -- write bytes to IFF file chunk
497 
498 size_t
write(const void * buffer,size_t size)499 IFFByteStream::write(const void *buffer, size_t size)
500 {
501   if (! (ctx && dir > 0))
502     G_THROW( ERR_MSG("IFFByteStream.not_ready4") );
503   if (seekto > offset)
504     G_THROW( ERR_MSG("IFFByteStream.cant_write") );
505   size_t bytes = bs->write(buffer, size);
506   offset += bytes;
507   return bytes;
508 }
509 
510 // IFFByteStream::tell
511 // -- tell position
512 
513 long
tell() const514 IFFByteStream::tell() const
515 {
516   return (seekto>offset)?seekto:offset;
517 }
518 
519 bool
compare(IFFByteStream & iff)520 IFFByteStream::compare(IFFByteStream &iff)
521 {
522   bool retval=(&iff == this);
523   if(!retval)
524   {
525     GUTF8String chkid1, chkid2;
526     int size;
527     while((size=get_chunk(chkid1)) == iff.get_chunk(chkid2))
528     {
529       if(chkid1 != chkid2)
530       {
531         break;
532       }
533       if(!size)
534       {
535         retval=true;
536         break;
537       }
538       char buf[4096];
539       int len;
540       while((len=read(buf,sizeof(buf))))
541       {
542         int s=0;
543         char buf2[sizeof(buf)];
544         while(s<len)
545         {
546           const int i=iff.read(buf2+s,len-s);
547           if(!i)
548             break;
549           s+=i;
550         }
551         if((s != len)||memcmp(buf,buf2,len))
552           break;
553       }
554       if(len)
555         break;
556       iff.close_chunk();
557       close_chunk();
558     }
559   }
560   return retval;
561 }
562 
563 
564 #ifdef HAVE_NAMESPACES
565 }
566 # ifndef NOT_USING_DJVU_NAMESPACE
567 using namespace DJVU;
568 # endif
569 #endif
570