1 /* This file is part of the wvWare 2 project
2    Copyright (C) 2002-2003 KO GmbH <jean.nicolas.artaud@kogmbh.>
3    Copyright (C) 2010, 2011 Matus Uzak <matus.uzak@ixonos.com>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License version 2 as published by the Free Software Foundation.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02111-1307, USA.
18 */
19 
20 #include "bookmark.h"
21 #include "word_helper.h"
22 #include "word97_generated.h"
23 #include "olestream.h"
24 
25 #include "wvlog.h"
26 #include <QList>
27 
28 using namespace wvWare;
29 
Bookmarks(OLEStreamReader * tableStream,const Word97::FIB & fib)30 Bookmarks::Bookmarks( OLEStreamReader* tableStream, const Word97::FIB& fib ) :
31     m_start(0), m_startIt(0), m_end(0), m_endIt(0), m_nFib(fib.nFib)
32 {
33 #ifdef WV2_DEBUG_BOOKMARK
34     wvlog   << endl
35             << "   fcPlcfbkf=" << fib.fcPlcfbkf << " lcbPlcfbkf=" << fib.lcbPlcfbkf << endl
36             << "   fcPlcfbkl=" << fib.fcPlcfbkl << " lcbPlcfbkl=" << fib.lcbPlcfbkl << endl
37             << "   lcbSttbfbkmk=" << fib.fcSttbfbkmk << " lcbSttbfbkmk=" << fib.lcbSttbfbkmk << endl;
38 #endif
39 
40     tableStream->push();
41 
42     if (fib.lcbPlcfbkf != 0)
43     {
44         tableStream->seek( fib.fcPlcfbkf, WV2_SEEK_SET );
45 
46         m_start = new PLCF<Word97::BKF>(fib.lcbPlcfbkf, tableStream);
47         m_startIt = new PLCFIterator<Word97::BKF>(*m_start);
48 
49 #ifdef WV2_DEBUG_BOOKMARK
50         wvlog << "Num. of bookmarks to start: " << m_start->count() << endl;
51         wvlog << "m_start init done" << endl;
52         m_start->dumpCPs();
53 #endif
54     }
55 
56     if ( fib.lcbSttbfbkmk != 0 )
57     {
58         if ( static_cast<U32>( tableStream->tell() ) != fib.fcSttbfbkmk ) {
59             tableStream->seek( fib.fcSttbfbkmk, WV2_SEEK_SET );
60         }
61         // The bookmark names in the STTBF are always Unicode, the lid doesn't matter
62         U16 usLid = 0x409;
63         STTBF* name = new STTBF( usLid, tableStream, false );
64 
65 #ifdef WV2_DEBUG_BOOKMARK
66         name->dumpStrings();
67 #endif
68         for (uint i = 0; i < name->count(); i++ ) {
69             m_name.push_back(name->stringAt(i));
70         }
71 
72         delete name;
73     }
74 
75     //The BKL is no longer stored in the plcfbkl or plcfatnbkl, and is instead
76     //reconstructed from the plcfbkf or plcfatnbkf when the file is opened.
77     //Microsoft Word 97 (aka Version 8)
78 
79     //TODO: Reconstruct BKL!
80 
81     if (fib.lcbPlcfbkl != 0)
82     {
83         int count = 0;
84         tableStream->seek( fib.fcPlcfbkl, WV2_SEEK_SET );
85 
86         //Word Version 6,7
87         if ( fib.nFib < Word8nFib ) {
88             m_end = new PLCF<Word97::BKL>(fib.lcbPlcfbkl, tableStream);
89             m_endIt = new PLCFIterator<Word97::BKL>(*m_end);
90             count = m_end->count();
91         } else {
92             count = (fib.lcbPlcfbkl - 4) / 4;
93             for ( int i = 0; i < count + 1; i++ ) {
94                 m_endCP.push_back( tableStream->readU32() );
95             }
96         }
97 
98 #ifdef WV2_DEBUG_BOOKMARK
99         wvlog << "Num. of bookmarks to end: " << count << endl;
100         wvlog << "m_end/m_endCP init done" << endl;
101 
102         if ( fib.nFib < Word8nFib ) {
103             m_end->dumpCPs();
104         } else {
105             for ( uint i = 0; i < m_endCP.size(); i++ ) {
106                 wvlog << "dumpCPs:   " << m_endCP[i] << endl;
107             }
108         }
109 #endif
110     }
111 
112 #ifdef WV2_DEBUG_BOOKMARK
113     wvlog << "Bookmark init done" << endl;
114 #endif
115 
116     tableStream->pop();
117 
118     U16 num = 0;
119     if (!valid(num, fib.ccpText)) {
120         wvlog << "Num. of invalid bookmarks:" << num;
121     }
122 
123     //using custom bookmark names if missing!
124     m_nameIt = m_name.begin();
125 }
126 
~Bookmarks()127 Bookmarks::~Bookmarks()
128 {
129     if ( m_nFib < Word8nFib ) {
130         delete m_endIt;
131         delete m_end;
132     }
133     delete m_startIt;
134     delete m_start;
135 }
136 
bookmark(const U32 globalCP,bool & ok)137 BookmarkData Bookmarks::bookmark( const U32 globalCP, bool& ok )
138 {
139 #ifdef WV2_DEBUG_BOOKMARK
140     wvlog << " globalCP=" << globalCP << endl;
141 #endif
142     ok = false;
143     if ( (m_startIt && m_startIt->current()) &&
144          (m_startIt->currentStart() == globalCP) &&
145          (m_nameIt != m_name.end()) )
146     {
147         if (m_valid.isEmpty()) {
148             wvlog << "BUG: m_valid empty?";
149         } else if (m_valid.first()) {
150             ok = true;
151         }
152 
153         U32 start = m_startIt->currentStart();
154         U32 end = start;
155 
156         if (m_nFib < Word8nFib) {
157             end = m_endIt->currentStart();
158             ++( *m_endIt );
159         } else {
160             U16 ibkl = m_startIt->current()->ibkl;
161             end = m_endCP[ibkl];
162         }
163 
164         ++( *m_startIt );
165         m_valid.removeFirst();
166 
167         UString name = *m_nameIt;
168         ++m_nameIt;
169 
170 #ifdef WV2_DEBUG_BOOKMARK
171         wvlog << "start = " << start << endl;
172         wvlog << "end = " << end << endl;
173         wvlog << "name = " << name.ascii() << endl;
174         wvlog << "valid = " << ok << endl;
175 #endif
176         return BookmarkData( start, end, name );
177     }
178     return BookmarkData( 0, 0, wvWare::UString("") );
179 }
180 
bookmark(const UString & name,bool & ok) const181 BookmarkData Bookmarks::bookmark(const UString& name, bool& ok ) const
182 {
183     std::vector<UString>::const_iterator nameIt = m_name.begin();
184     PLCFIterator<Word97::BKF> startIt(*m_start);
185 
186     PLCFIterator<Word97::BKL>* endIt = 0;
187     if (m_nFib < Word8nFib) {
188         endIt = new PLCFIterator<Word97::BKL>(*m_end);
189     }
190 
191     while (startIt.current()) {
192         if (*nameIt == name) {
193             U32 start = startIt.currentStart();
194             U32 end = start;
195             if (m_nFib < Word8nFib) {
196                 end = endIt->currentStart();
197                 delete endIt;
198             } else {
199                 U16 ibkl = startIt.current()->ibkl;
200                 end = m_endCP[ibkl];
201             }
202             ok = true;
203             return BookmarkData( start, end, name );
204         }
205         ++startIt;
206         ++nameIt;
207 
208         if (m_nFib < Word8nFib) {
209             ++( *endIt );
210         }
211     }
212     if (m_nFib < Word8nFib) {
213         delete endIt;
214     }
215     ok = false;
216     return BookmarkData( 0, 0, UString("") );
217 }
218 
nextBookmarkStart()219 U32 Bookmarks::nextBookmarkStart()
220 {
221     U32 ret = 0xffffffff;
222 
223     //find the next valid bookmark
224     while (m_startIt && m_startIt->current()) {
225 
226         if (m_valid.isEmpty()) {
227             wvlog << "BUG: m_valid empty?";
228             break;
229         }
230         else if (m_valid.first()) {
231             ret = m_startIt->currentStart();
232             break;
233         } else {
234             //NOTE: Add logic to process invalid bookmarks here.
235 
236             if (m_nFib < Word8nFib) {
237                 ++( *m_endIt );
238             }
239             m_valid.removeFirst();
240             ++( *m_startIt );
241             ++m_nameIt;
242 
243 #ifdef WV2_DEBUG_BOOKMARK
244             wvlog << "Warning: Skipped invalid bookmark!";
245 #endif
246         }
247     }
248     return ret;
249 }
250 
nextBookmarkEnd() const251 U32 Bookmarks::nextBookmarkEnd() const
252 {
253     U32 ret = 0xffffffff;
254 
255     if (m_nFib < Word8nFib) {
256         if (m_endIt && m_endIt->current()) {
257             ret = m_endIt->currentStart();
258         }
259     } else {
260         if (m_startIt && m_startIt->current()) {
261             U16 ibkl = (m_startIt->current())->ibkl;
262             ret = m_endCP[ibkl];
263         }
264     }
265     return ret;
266 }
267 
check(U32 globalCP)268 void Bookmarks::check( U32 globalCP )
269 {
270     while (nextBookmarkStart() < globalCP)
271     {
272         if (m_nFib < Word8nFib) {
273             ++( *m_endIt );
274         }
275         ++( *m_startIt );
276         ++m_nameIt;
277 
278         if (m_valid.isEmpty()) {
279             wvlog << "BUG: m_valid empty?";
280         } else {
281             m_valid.removeFirst();
282         }
283 
284 #ifdef WV2_DEBUG_BOOKMARK
285         wvlog << "Bookmark skipped! CP:" << globalCP;
286 #endif
287     }
288 }
289 
valid(U16 & num,const U32 ccpText)290 bool Bookmarks::valid(U16 &num, const U32 ccpText)
291 {
292     PLCFIterator<Word97::BKF> startIt(*m_start);
293     QList<U16> ibkls;
294     bool ret = true;
295     U16 ibkl = 0;
296     num = 0;
297 
298 #ifdef WV2_DEBUG_BOOKMARK
299     U16 n = 1;
300 #endif
301 
302     if (m_nFib < Word8nFib) {
303         PLCFIterator<Word97::BKL> endIt(*m_end);
304         while (startIt.current()) {
305             if ( !endIt.current() ||
306                  (startIt.currentStart() > endIt.currentStart()) ||
307                  (startIt.currentStart() > ccpText) )
308             {
309                 m_valid.append(false);
310                 ret = false;
311                 num++;
312 #ifdef WV2_DEBUG_BOOKMARK
313                 wvlog << "bkmk" << n << ": (startCP > endCP) || endCP missing";
314 #endif
315             } else {
316                 m_valid.append(true);
317             }
318 #ifdef WV2_DEBUG_BOOKMARK
319             n++;
320 #endif
321             ++startIt;
322             ++endIt;
323         }
324     } else {
325         while (startIt.current()) {
326             ibkl = (startIt.current())->ibkl;
327             //MUST be unique for all FBKFs inside a given PlcfBkf
328             if (ibkls.contains(ibkl) || (ibkl > m_endCP.size())) {
329                 m_valid.append(false);
330                 ret = false;
331                 num++;
332 #ifdef WV2_DEBUG_BOOKMARK
333                 wvlog << "bkmk" << n << ": ibkl invalid!";
334                 n++;
335 #endif
336                 ++startIt;
337                 continue;
338             } else {
339                 ibkls.append(ibkl);
340             }
341 
342             if ( (startIt.currentStart() > m_endCP[ibkl]) ||
343                  (startIt.currentStart() > ccpText) )
344             {
345                 m_valid.append(false);
346                 ret = false;
347                 num++;
348 #ifdef WV2_DEBUG_BOOKMARK
349         wvlog << "bkmk" << n << ": startCP > endCP (" <<
350                     startIt.currentStart() << "|" << m_endCP[ibkl] << ")";
351 #endif
352             } else {
353                 m_valid.append(true);
354             }
355 #ifdef WV2_DEBUG_BOOKMARK
356             n++;
357 #endif
358             ++startIt;
359         }
360     }
361 
362     //check bookmark names
363     for (uint i = 0; i < m_name.size(); i++) {
364         if ( (m_name[i] == UString::null) ) {
365             m_name[i] = UString().from(i + 1);
366         }
367     }
368     if (m_name.size() < m_start->count()) {
369         for (uint i = m_name.size(); i < m_start->count(); i++) {
370             m_name.push_back(UString().from(i + 1));
371         }
372 #ifdef WV2_DEBUG_BOOKMARK
373         wvlog << "Warning: bookmark names missing!  Using custom names.";
374         wvlog << "Num. of bookmark names:" << m_name.size();
375 
376         std::vector<UString>::const_iterator it = m_name.begin();
377         while(it != m_name.end()) {
378             wvlog << "bkmk name:" << (*it).ascii();
379             ++it;
380         }
381 #endif
382     }
383     return ret;
384 }
385