source: trunk/libdjvu/DjVmDir.cpp @ 199

Last change on this file since 199 was 17, checked in by Eugene Romanenko, 16 years ago

update makefiles, remove absolute paths, update djvulibre to version 3.5.17

File size: 24.9 KB
Line 
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, Version 2. The license should have
9//C- accompanied the software or you may obtain a copy of the license
10//C- from the Free Software Foundation at http://www.fsf.org .
11//C-
12//C- This program is distributed in the hope that it will be useful,
13//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
14//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//C- GNU General Public License for more details.
16//C-
17//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
18//C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech
19//C- Software authorized us to replace the original DjVu(r) Reference
20//C- Library notice by the following text (see doc/lizard2002.djvu):
21//C-
22//C-  ------------------------------------------------------------------
23//C- | DjVu (r) Reference Library (v. 3.5)
24//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
25//C- | The DjVu Reference Library is protected by U.S. Pat. No.
26//C- | 6,058,214 and patents pending.
27//C- |
28//C- | This software is subject to, and may be distributed under, the
29//C- | GNU General Public License, Version 2. The license should have
30//C- | accompanied the software or you may obtain a copy of the license
31//C- | from the Free Software Foundation at http://www.fsf.org .
32//C- |
33//C- | The computer code originally released by LizardTech under this
34//C- | license and unmodified by other parties is deemed "the LIZARDTECH
35//C- | ORIGINAL CODE."  Subject to any third party intellectual property
36//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
37//C- | non-exclusive license to make, use, sell, or otherwise dispose of
38//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
39//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
40//C- | General Public License.   This grant only confers the right to
41//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
42//C- | the extent such infringement is reasonably necessary to enable
43//C- | recipient to make, have made, practice, sell, or otherwise dispose
44//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
45//C- | any greater extent that may be necessary to utilize further
46//C- | modifications or combinations.
47//C- |
48//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
49//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
50//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
51//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
52//C- +------------------------------------------------------------------
53//
54// $Id: DjVmDir.cpp,v 1.11 2005/12/30 15:22:16 leonb Exp $
55// $Name:  $
56
57#ifdef HAVE_CONFIG_H
58# include "config.h"
59#endif
60#if NEED_GNUG_PRAGMAS
61# pragma implementation
62#endif
63
64#include "DjVmDir.h"
65#include "BSByteStream.h"
66#include "GURL.h"
67#include "debug.h"
68
69#include <ctype.h>
70
71
72#ifdef HAVE_NAMESPACES
73namespace DJVU {
74# ifdef NOT_DEFINED // Just to fool emacs c++ mode
75}
76#endif
77#endif
78
79
80GP<DjVmDir::File>
81DjVmDir::File::create(const GUTF8String &load_name,
82  const GUTF8String &save_name, const GUTF8String &title,
83  const FILE_TYPE file_type)
84{
85  File *file_ptr=new File();
86  GP<File> file=file_ptr;
87  file_ptr->set_load_name(load_name);
88  file_ptr->set_save_name(save_name);
89  file_ptr->set_title(title);
90  file_ptr->flags=(file_type & TYPE_MASK);
91  return file;
92}
93
94const GUTF8String &
95DjVmDir::File::check_save_name(const bool xis_bundled)
96{
97  if(!xis_bundled && !valid_name)
98  {
99    GUTF8String retval=name.length()?name:id;
100    if(GUTF8String(GNativeString(retval)) != retval)
101    {
102      const_cast<bool &>(valid_name)=true;
103      char *buf;
104      GPBuffer<char> gbuf(buf,2*retval.length()+1);
105      char *s=buf;
106      int i=0;
107      for(char c=retval[i++];c;)
108      {
109        static const char hex[]="0123456789ABCDEF";
110        int len=retval.nextChar(i)-i;
111        if(len>1 || ((len == 1)&&(c&0x80)))
112        {
113          do
114          {
115            s++[0]=hex[(c>>4)&0xf];
116            s++[0]=hex[(c&0xf)];
117            c=retval[i++];
118          } while(c && ((--len) > 0));
119        }else
120        {
121          s++[0]=c;
122          c=retval[i++];
123        }
124      }
125      s++[0]=0;
126      oldname=retval;
127      name=buf;
128    }
129    const_cast<bool &>(valid_name)=true;
130  }
131  return *(name.length()?&name:&id);
132}
133
134const GUTF8String &
135DjVmDir::File::get_save_name(void) const
136{
137  return *(name.length()?&name:&id);
138}
139
140void
141DjVmDir::File::set_load_name(const GUTF8String &xid)
142{
143  GURL url=GURL::UTF8(xid);
144  if(!url.is_valid())
145  {
146    url=GURL::Filename::UTF8(xid);
147  }
148  id=url.fname();
149}
150
151void
152DjVmDir::File::set_save_name(const GUTF8String &xname)
153{
154  GURL url;
155  valid_name=false;
156  if(!xname.length())
157  {
158    GURL url=GURL::UTF8(id);
159    if(!url.is_valid())
160    {
161      name=id;
162    }else
163    {
164      name=url.fname();
165    }
166  }else
167  {
168    GURL url=GURL::UTF8(xname);
169    if(!url.is_valid())
170    {
171      url=GURL::Filename::UTF8(xname);
172    }
173    name=url.fname();
174  }
175  oldname="";
176}
177
178/* DjVmDir::File */
179
180DjVmDir::File::File(void) : offset(0), size(0), valid_name(false),
181   flags(0), page_num(-1) { }
182
183GUTF8String
184DjVmDir::File::get_str_type(void) const
185{
186   GUTF8String type;
187   switch(flags & TYPE_MASK)
188   {
189      case INCLUDE:
190        type="INCLUDE";
191        break;
192      case PAGE:
193        type="PAGE";
194        break;
195      case THUMBNAILS:
196        type="THUMBNAILS";
197        break;
198      case SHARED_ANNO:
199        type="SHARED_ANNO";
200        break;
201      default:
202        //  Internal error: please modify DjVmDir::File::get_str_type()
203        //  to contain all possible File types.
204              G_THROW( ERR_MSG("DjVmDir.get_str_type") );
205   }
206   return type;
207}
208
209
210const int DjVmDir::version=1;
211
212void 
213DjVmDir::decode(const GP<ByteStream> &gstr)
214{
215   ByteStream &str=*gstr;
216   DEBUG_MSG("DjVmDir::decode(): decoding contents of 'DIRM' chunk...\n");
217   DEBUG_MAKE_INDENT(3);
218   
219   GCriticalSectionLock lock(&class_lock);
220
221   GPosition pos;
222
223   files_list.empty();
224   page2file.resize(-1);
225   name2file.empty();
226   id2file.empty();
227   title2file.empty();
228
229   int ver=str.read8();
230   bool bundled=(ver & 0x80)!=0;
231   ver&=0x7f;
232
233   DEBUG_MSG("DIRM version=" << ver << ", our version=" << version << "\n");
234   if (ver>version)
235      G_THROW( ERR_MSG("DjVmDir.version_error") "\t" 
236               + GUTF8String(version) + "\t" + GUTF8String(ver));
237   // Unable to read DJVM directories of versions higher than xxx
238   // Data version number is yyy.
239   DEBUG_MSG("bundled directory=" << bundled << "\n");
240   DEBUG_MSG("reading the directory records...\n");
241   int files=str.read16();
242   DEBUG_MSG("number of files=" << files << "\n");
243
244   if (files)
245   {
246      DEBUG_MSG("reading offsets (and sizes for ver==0)\n");
247      for(int nfile=0;nfile<files;nfile++)
248      {
249         GP<File> file=new File();
250         files_list.append(file);
251         if (bundled)
252         {
253            file->offset=str.read32();
254            if (ver==0)
255              file->size=str.read24();
256            if (file->offset==0)
257               G_THROW( ERR_MSG("DjVmDir.no_indirect") );
258         } else
259         {
260           file->offset=file->size=0;
261         }
262      }
263
264      GP<ByteStream> gbs_str=BSByteStream::create(gstr);
265      ByteStream &bs_str=*gbs_str;
266      if (ver>0)
267      {
268         DEBUG_MSG("reading and decompressing sizes...\n");
269         for(GPosition pos=files_list;pos;++pos)
270            files_list[pos]->size=bs_str.read24();
271      }
272         
273      DEBUG_MSG("reading and decompressing flags...\n");
274      for(pos=files_list;pos;++pos)
275         files_list[pos]->flags=bs_str.read8();
276
277      if (!ver)
278      {
279         DEBUG_MSG("converting flags from version 0...\n");
280         for(pos=files_list;pos;++pos)
281         {
282            unsigned char flags_0=files_list[pos]->flags;
283            unsigned char flags_1;
284            flags_1=(flags_0 & File::IS_PAGE_0)?(File::PAGE):(File::INCLUDE);
285            if (flags_0 & File::HAS_NAME_0)
286              flags_1|=File::HAS_NAME;
287            if (flags_0 & File::HAS_TITLE_0)
288              flags_1|=File::HAS_TITLE;
289            files_list[pos]->flags=flags_1;
290         }
291      }
292   
293      DEBUG_MSG("reading and decompressing names...\n");
294      GTArray<char> strings;
295      char buffer[1024];
296      int length;
297      while((length=bs_str.read(buffer, 1024)))
298      {
299         int strings_size=strings.size();
300         strings.resize(strings_size+length-1);
301         memcpy((char*) strings+strings_size, buffer, length);
302      }
303      DEBUG_MSG("size of decompressed names block=" << strings.size() << "\n");
304   
305         // Copy names into the files
306      const char * ptr=strings;
307      for(pos=files_list;pos;++pos)
308      {
309         GP<File> file=files_list[pos];
310
311         file->id=ptr;
312         ptr+=file->id.length()+1;
313         if (file->flags & File::HAS_NAME)
314         {
315            file->name=ptr;
316            ptr+=file->name.length()+1;
317         } else
318         {
319            file->name=file->id;
320         }
321         if (file->flags & File::HAS_TITLE)
322         {
323            file->title=ptr;
324       ptr+=file->title.length()+1;
325         } else
326       file->title=file->id;
327   /* msr debug:  multipage file, file->title is null. 
328         DEBUG_MSG(file->name << ", " << file->id << ", " << file->title << ", " <<
329                   file->offset << ", " << file->size << ", " <<
330                   file->is_page() << "\n"); */
331      }
332
333         // Check that there is only one file with SHARED_ANNO flag on
334      int shared_anno_cnt=0;
335      for(pos=files_list;pos;++pos)
336      {
337         if (files_list[pos]->is_shared_anno())
338         {
339            shared_anno_cnt++;
340         }
341      }
342      if (shared_anno_cnt>1)
343        G_THROW( ERR_MSG("DjVmDir.corrupt") );
344
345         // Now generate page=>file array for direct access
346      int pages=0;
347      for(pos=files_list;pos;++pos)
348              pages+=files_list[pos]->is_page() ? 1 : 0;
349      DEBUG_MSG("got " << pages << " pages\n");
350      page2file.resize(pages-1);
351      int page_num=0;
352      for(pos=files_list;pos;++pos)
353      {
354               GP<File> file=files_list[pos];
355               if (file->is_page())
356               {
357                  page2file[page_num]=file;
358                  file->page_num=page_num++;
359               }
360      }
361
362         // Generate name2file map
363      for(pos=files_list;pos;++pos)
364      {
365               GP<File> file=files_list[pos];
366               if (name2file.contains(file->name))
367                  G_THROW( ERR_MSG("DjVmDir.dupl_name") "\t" + file->name );
368               name2file[file->name]=file;
369      }
370
371         // Generate id2file map
372      for(pos=files_list;pos;++pos)
373      {
374               GP<File> file=files_list[pos];
375               if (id2file.contains(file->id))
376                  G_THROW( ERR_MSG("DjVmDir.dupl_id") "\t" + file->id);
377               id2file[file->id]=file;
378      }
379
380         // Generate title2file map
381      for(pos=files_list;pos;++pos)
382      {
383               GP<File> file=files_list[pos];
384               if (file->title.length())
385               {
386                  if (title2file.contains(file->title))
387                     G_THROW( ERR_MSG("DjVmDir.dupl_title") "\t" + file->title);
388                  title2file[file->title]=file;
389               }
390      }
391   }
392}
393
394
395void
396DjVmDir::encode(const GP<ByteStream> &gstr, const bool do_rename) const
397{
398  bool bundled = true;
399  GPosition pos = files_list;
400  if (files_list.size() && !files_list[pos]->offset)
401    bundled = false;
402  for (pos=files_list; pos; ++pos)
403    if ( !bundled !=  !files_list[pos]->offset)
404      //  There directory contains both indirect and bundled records.
405      G_THROW( ERR_MSG("DjVmDir.bad_dir") );
406  // Do the real work
407  encode(gstr, bundled, do_rename);
408}
409
410void
411DjVmDir::encode(const GP<ByteStream> &gstr, const bool bundled, const bool do_rename) const
412{
413  ByteStream &str=*gstr;
414  DEBUG_MSG("DjVmDir::encode(): encoding contents of the 'DIRM' chunk do_rename=" << do_rename << "\n");
415  DEBUG_MAKE_INDENT(3);
416   
417  GCriticalSectionLock lock((GCriticalSection *) &class_lock);
418  GPosition pos;
419
420  DEBUG_MSG("encoding version number=" << version << ", bundled=" << bundled << "\n");
421  str.write8(version | ((int) bundled<< 7));
422   
423  DEBUG_MSG("storing the number of records=" << files_list.size() << "\n");
424  str.write16(files_list.size());
425
426  if (files_list.size())
427    {
428      // Check that there is only one file with shared annotations
429      int shared_anno_cnt=0;
430      for (pos=files_list; pos; ++pos)
431        if (files_list[pos]->is_shared_anno())
432          shared_anno_cnt++;
433      if (shared_anno_cnt>1)
434        G_THROW( ERR_MSG("DjVmDir.multi_save") );
435     
436      if (bundled)
437        {
438          // We need to store offsets uncompressed. That's because when
439          // we save a DjVmDoc, we first compress the DjVmDir with dummy
440          // offsets and after computing the real offsets we rewrite the
441          // DjVmDir, which should not change its size during this operation
442          DEBUG_MSG("storing offsets for every record\n");
443          for (pos=files_list; pos; ++pos)
444            {
445              GP<File> file=files_list[pos];
446              if (!file->offset)
447                // The directory contains record without offset
448                G_THROW( ERR_MSG("DjVmDir.bad_dir") );
449              str.write32(file->offset);
450            }
451        }
452     
453      GP<ByteStream> gbs_str=BSByteStream::create(gstr, 50);
454      ByteStream &bs_str=*gbs_str;
455      DEBUG_MSG("storing and compressing sizes for every record\n");
456      for (pos=files_list; pos; ++pos)
457        {
458          const GP<File> file(files_list[pos]);
459          bs_str.write24(file->size);
460        }
461      DEBUG_MSG("storing and compressing flags for every record\n");
462      const bool xdo_rename=(do_rename||!bundled);
463      for(pos=files_list;pos;++pos)
464        {
465          const GP<File> file(files_list[pos]);
466          if(xdo_rename)
467            {
468              const GUTF8String new_id = file->name;
469              if (! new_id)
470                if(!file->oldname.length() || file->oldname == new_id)
471                  file->flags &= ~File::HAS_NAME;
472                else
473                  file->flags |= File::HAS_NAME;
474            }
475          else 
476            {
477              if (!file->name.length() || file->name == file->id)
478                file->flags &= ~File::HAS_NAME;
479              else
480                file->flags |= File::HAS_NAME;
481            }
482          if (file->title.length() && (file->title!=file->id))
483            file->flags |= File::HAS_TITLE;
484          else
485            file->flags &= ~File::HAS_TITLE;
486
487       bs_str.write8(file->flags);
488     }
489
490     DEBUG_MSG("storing and compressing names...\n");
491     for(pos=files_list;pos;++pos)
492     {
493         GP<File> file=files_list[pos];
494         GUTF8String id;
495         GUTF8String name;
496         GUTF8String title;
497         if (xdo_rename)
498           {
499             id = file->name;
500             if (! id)
501               id = file->id;
502             if ((file->flags) & File::HAS_NAME)
503               name = file->oldname;
504           }
505         else
506           {
507             id=file->id;
508             if ((file->flags) & File::HAS_NAME)
509               name = file->name;
510           }
511         if ((file->flags) & File::HAS_TITLE)
512           title = file->title;
513         DEBUG_MSG("rename=" <<xdo_rename<<" id='" << id << "' name='" << name << "' title='" << title << "'\n");
514         bs_str.writestring(id);
515         bs_str.write8(0);
516         if (name.length())
517           {
518             bs_str.writestring(name);
519             bs_str.write8(0);
520           }
521         if (title.length())
522           {
523             bs_str.writestring(title);
524             bs_str.write8(0);
525           }
526     }
527    }
528  DEBUG_MSG("done\n");
529}
530
531GP<DjVmDir::File>
532DjVmDir::page_to_file(int page_num) const
533{
534   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
535
536   return (page_num<page2file.size())?page2file[page_num]:(GP<DjVmDir::File>(0));
537}
538
539GP<DjVmDir::File>
540DjVmDir::name_to_file(const GUTF8String & name) const
541{
542   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
543
544   GPosition pos;
545   return (name2file.contains(name, pos))?name2file[pos]:(GP<DjVmDir::File>(0));
546}
547
548GP<DjVmDir::File>
549DjVmDir::id_to_file(const GUTF8String &id) const
550{
551   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
552
553   GPosition pos;
554   return (id2file.contains(id, pos))?id2file[pos]:(GP<DjVmDir::File>(0));
555}
556
557GP<DjVmDir::File>
558DjVmDir::title_to_file(const GUTF8String &title) const
559{
560   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
561
562   GPosition pos;
563   return (title2file.contains(title, pos))?title2file[pos]:(GP<DjVmDir::File>(0));
564}
565
566GP<DjVmDir::File>
567DjVmDir::pos_to_file(int fileno, int *ppageno) const
568{
569  GCriticalSectionLock lock((GCriticalSection *) &class_lock);
570  GPosition pos = files_list;
571  int pageno = 0;
572  while (pos && --fileno >= 0) {
573    if (files_list[pos]->is_page())
574      ++pageno;
575    ++pos;
576  }
577  if (!pos)
578    return 0;
579  if (ppageno)
580    *ppageno = pageno;
581  return files_list[pos];
582}
583
584GPList<DjVmDir::File>
585DjVmDir::get_files_list(void) const
586{
587  GCriticalSectionLock lock((GCriticalSection *) &class_lock);
588  return files_list;
589}
590
591int
592DjVmDir::get_files_num(void) const
593{
594  GCriticalSectionLock lock((GCriticalSection *) &class_lock);
595  return files_list.size();
596}
597
598int
599DjVmDir::get_pages_num(void) const
600{
601   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
602   return page2file.size();
603}
604
605int
606DjVmDir::get_file_pos(const File * f) const
607{
608   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
609   int cnt;
610   GPosition pos;
611   for(pos=files_list, cnt=0;pos&&(files_list[pos]!=f);++pos, cnt++)
612                   continue;
613   return (pos)?cnt:(-1);
614}
615
616int
617DjVmDir::get_page_pos(int page_num) const
618{
619   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
620   
621   GP<File> file=page_to_file(page_num);
622   return (file)?get_file_pos(file):(-1);
623}
624
625GP<DjVmDir::File>
626DjVmDir::get_shared_anno_file(void) const
627{
628   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
629
630   GP<File> file;
631   for(GPosition pos=files_list;pos;++pos)
632   {
633      GP<File> frec=files_list[pos];
634      if (frec->is_shared_anno())
635      {
636         file=frec;
637         break;
638      }
639   }
640   return file;
641}
642
643int
644DjVmDir::insert_file(const GP<File> & file, int pos_num)
645{
646   DEBUG_MSG("DjVmDir::insert_file(): name='" 
647             << file->name << "', pos=" << pos_num << "\n");
648   DEBUG_MAKE_INDENT(3);
649   
650   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
651   
652   if (pos_num<0)
653     pos_num=files_list.size();
654
655   //// Modify maps
656   //   if (! File::is_legal_id(file->id))
657   //     G_THROW( ERR_MSG("DjVmDir.bad_file") "\t" + file->id);
658   if (id2file.contains(file->id))
659     G_THROW( ERR_MSG("DjVmDir.dupl_id2") "\t" + file->id);
660   if (name2file.contains(file->name))
661     G_THROW( ERR_MSG("DjVmDir.dupl_name2") "\t" + file->name);
662   name2file[file->name]=file;
663   id2file[file->id]=file;
664   if (file->title.length())
665     {
666       if (title2file.contains(file->title)) 
667         // duplicate titles may become ok some day
668         G_THROW( ERR_MSG("DjVmDir.dupl_title2") "\t" + file->title);
669       title2file[file->title]=file;
670     }
671
672      // Make sure that there is no more than one file with shared annotations
673   if (file->is_shared_anno())
674   {
675      for(GPosition pos=files_list;pos;++pos)
676         if (files_list[pos]->is_shared_anno())
677            G_THROW( ERR_MSG("DjVmDir.multi_save2") );
678   }
679   
680      // Add the file to the list
681   int cnt;
682   GPosition pos;
683   for(pos=files_list, cnt=0;pos&&(cnt!=pos_num);++pos, cnt++)
684                   continue;
685   if (pos)
686     files_list.insert_before(pos, file);
687   else
688     files_list.append(file);
689
690   if (file->is_page())
691   {
692         // This file is also a page
693         // Count its number
694      int page_num=0;
695      for(pos=files_list;pos;++pos)
696      {
697         GP<File> &f=files_list[pos];
698         if (f==file)
699           break;
700         if (f->is_page())
701           page_num++;
702      }
703
704      int i;
705      page2file.resize(page2file.size());
706      for(i=page2file.size()-1;i>page_num;i--)
707         page2file[i]=page2file[i-1];
708      page2file[page_num]=file;
709      for(i=page_num;i<page2file.size();i++)
710         page2file[i]->page_num=i;
711   }
712   return pos_num;
713}
714
715void
716DjVmDir::delete_file(const GUTF8String &id)
717{
718   DEBUG_MSG("Deleting file with id='" << (const char *)id << "'\n");
719   DEBUG_MAKE_INDENT(3);
720
721   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
722   
723   for(GPosition pos=files_list;pos;++pos)
724   {
725      GP<File> & f=files_list[pos];
726      if (id == f->id)
727      {
728         name2file.del(f->name);
729         id2file.del(f->id);
730         title2file.del(f->title);
731         if (f->is_page())
732         {
733            for(int page=0;page<page2file.size();page++)
734            {
735               if (page2file[page]==f)
736               {
737                  int i;
738                  for(i=page;i<page2file.size()-1;i++)
739                     page2file[i]=page2file[i+1];
740                  page2file.resize(page2file.size()-2);
741                  for(i=page;i<page2file.size();i++)
742                     page2file[i]->page_num=i;
743                  break;
744               }
745            }
746         }
747         files_list.del(pos);
748         break;
749      }
750   }
751}
752
753void
754DjVmDir::set_file_name(const GUTF8String &id, const GUTF8String &name)
755{
756   DEBUG_MSG("DjVmDir::set_file_name(): id='" << id << "', name='" << name << "'\n");
757   DEBUG_MAKE_INDENT(3);
758   
759   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
760
761   GPosition pos;
762   
763      // First see, if the name is unique
764   for(pos=files_list;pos;++pos)
765   {
766      GP<File> file=files_list[pos];
767      if (file->id!=id && file->name==name)
768        G_THROW( ERR_MSG("DjVmDir.name_in_use") "\t" + GUTF8String(name));
769   }
770
771      // Check if ID is valid
772   if (!id2file.contains(id, pos))
773      G_THROW( ERR_MSG("DjVmDir.no_info") "\t" + GUTF8String(id));
774   GP<File> file=id2file[pos];
775   name2file.del(file->name);
776   file->name=name;
777   name2file[name]=file;
778}
779
780void
781DjVmDir::set_file_title(const GUTF8String &id, const GUTF8String &title)
782{
783   DEBUG_MSG("DjVmDir::set_file_title(): id='" << id << "', title='" << title << "'\n");
784   DEBUG_MAKE_INDENT(3);
785   
786   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
787
788   GPosition pos;
789   
790      // First see, if the title is unique
791   for(pos=files_list;pos;++pos)
792   {
793      GP<File> file=files_list[pos];
794      if (file->id!=id && file->title==title)
795        G_THROW( ERR_MSG("DjVmDir.title_in_use") "\t" + GUTF8String(title));
796   }
797
798      // Check if ID is valid
799   if (!id2file.contains(id, pos))
800      G_THROW( ERR_MSG("DjVmDir.no_info") "\t" + GUTF8String(id));
801   GP<File> file=id2file[pos];
802   title2file.del(file->title);
803   file->title=title;
804   title2file[title]=file;
805}
806
807GPList<DjVmDir::File>
808DjVmDir::resolve_duplicates(const bool save_as_bundled)
809{
810  GCriticalSectionLock lock((GCriticalSection *) &class_lock);
811  // Make sure all the filenames are unique.
812  GPosition pos;
813  GMap<GUTF8String,void *> save_map;
814  GMap<GUTF8String,GPList<DjVmDir::File> > conflicts;
815  for(pos=files_list;pos;++pos)
816  {
817    const GUTF8String save_name=files_list[pos]->check_save_name(save_as_bundled).downcase();
818    if(save_map.contains(save_name))
819    {
820      conflicts[save_name].append(files_list[pos]);
821    }else
822    {
823      save_map[save_name]=0;
824    }
825  }
826  for(pos=conflicts;pos;++pos)
827  {
828    const GUTF8String &save_name=conflicts.key(pos);
829    const int dot=save_name.rsearch('.',0);
830    GPList<DjVmDir::File> &cfiles=conflicts[pos];
831    int count=1;
832    for(GPosition qpos=cfiles;qpos;++qpos)
833    {
834      GUTF8String new_name=cfiles[qpos]->get_load_name();
835      if((new_name != GUTF8String(GNativeString(new_name)))
836        ||conflicts.contains(new_name))
837      {
838        do
839        {
840          new_name=(dot<0)
841            ?(save_name+"-"+GUTF8String(count++))
842            :(save_name.substr(0,dot)+"-"+GUTF8String(count++)+
843              save_name.substr(dot,(unsigned int)(-1)));
844        } while(save_map.contains(new_name.downcase()));
845      }
846      cfiles[qpos]->set_save_name(new_name);
847      save_map[new_name]=0;
848    }
849  }
850  return files_list;
851}
852
853
854#ifdef HAVE_NAMESPACES
855}
856# ifndef NOT_USING_DJVU_NAMESPACE
857using namespace DJVU;
858# endif
859#endif
Note: See TracBrowser for help on using the repository browser.