kpilot

DOC-converter.cc

Go to the documentation of this file.
00001 /* KPilot
00002 **
00003 ** Copyright (C) 2002-2003 by Reinhold Kainhofer
00004 **
00005 ** The doc converter synchronizes text files on the PC with DOC databases on the Palm
00006 */
00007 
00008 /*
00009 ** This program is free software; you can redistribute it and/or modify
00010 ** it under the terms of the GNU General Public License as published by
00011 ** the Free Software Foundation; either version 2 of the License, or
00012 ** (at your option) any later version.
00013 **
00014 ** This program is distributed in the hope that it will be useful,
00015 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017 ** GNU General Public License for more details.
00018 **
00019 ** You should have received a copy of the GNU General Public License
00020 ** along with this program in a file called COPYING; if not, write to
00021 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00022 ** MA 02110-1301, USA.
00023 */
00024 
00025 /*
00026 ** Bug reports and questions can be sent to kde-pim@kde.org
00027 */
00028 
00029 
00030 #include "options.h"
00031 #include "DOC-converter.moc"
00032 
00033 #include <qdir.h>
00034 #include <qfileinfo.h>
00035 #include <qregexp.h>
00036 #include <qsortedlist.h>
00037 
00038 #include <pilotDatabase.h>
00039 #include <pilotLocalDatabase.h>
00040 #include <pilotSerialDatabase.h>
00041 
00042 #include "pilotDOCHead.h"
00043 #include "pilotDOCEntry.h"
00044 #include "pilotDOCBookmark.h"
00045 
00046 
00047 
00048 #define min(a,b) (a<b)?(a):(b)
00049 
00050 
00051 
00052 /****************************************************************************************************
00053  *  various bookmark classes. Most important is the bmkList  findMatches(QString) function,
00054  *  which needs to return a list of all bookmarks found for the given bookmark expression.
00055  *  A bookmark usually consists of a bookmark text and an offset into the text document.
00056  ****************************************************************************************************/
00057 
00058 
00059 bool docBookmark::compare_pos=true;
00060 
00061 bool operator< ( const docBookmark &s1, const docBookmark &s2)
00062 {
00063     if (docBookmark::compare_pos) { return s1.position<s2.position;}
00064     else {return s2.bmkName<s2.bmkName;}
00065 }
00066 
00067 bool operator== ( const docBookmark &s1, const docBookmark &s2)
00068 {
00069     return (s1.position==s2.position) && (s1.bmkName==s2.bmkName);
00070 }
00071 
00072 
00073 int docMatchBookmark::findMatches(QString doctext, bmkList &fBookmarks) {
00074     FUNCTIONSETUP;
00075 //  bmkList res;
00076     int pos = 0, nr=0, found=0;
00077 #ifdef DEBUG
00078     DEBUGKPILOT<<"Finding matches of "<<pattern<<endl;
00079 #endif
00080 
00081     while (pos >= 0 && found<to) {
00082         pos = doctext.find(pattern, pos);
00083 #ifdef DEBUG
00084         DEBUGKPILOT<<"Result of search: pos="<<pos<<endl;
00085 #endif
00086         if (pos >= 0)
00087         {
00088             ++found;
00089             if (found>=from && found<=to) {
00090                 fBookmarks.append(new docBookmark(pattern, pos));
00091                 ++nr;
00092 
00093             }
00094             ++pos;
00095         }
00096     }
00097     return nr;
00098 }
00099 
00100 
00101 
00102 int docRegExpBookmark::findMatches(QString doctext, bmkList &fBookmarks)
00103 {
00104     FUNCTIONSETUP;
00105 //  bmkList res;
00106     QRegExp rx(pattern);
00107     int pos = 0, nr=0, found=0;
00108 
00109     while (pos>=0 && found<=to) {
00110 #ifdef DEBUG
00111         DEBUGKPILOT<<"Searching for bookmark "<<pattern<<endl;
00112 #endif
00113         pos=rx.search(doctext, pos);
00114         if (pos > -1) {
00115             ++found;
00116             if (found>=from && found<to) {
00117                 if (capSubexpression>=0) {
00118                     fBookmarks.append(new docBookmark(/*bmkName.left(16)*/rx.cap(capSubexpression), pos));
00119                 } else {
00120                     // TODO: use the subexpressions from the regexp for the bmk name ($1..$9) (given as separate regexp)
00121                     QString bmkText(bmkName);
00122                     for (int i=0; i<=rx.numCaptures(); ++i) {
00123                         bmkText.replace(CSL1("$%1").arg(i), rx.cap(i));
00124                         bmkText.replace(CSL1("\\%1").arg(i), rx.cap(i));
00125                     }
00126                     fBookmarks.append(new docBookmark(bmkText.left(16), pos));
00127                 }
00128                 ++nr;
00129             }
00130             ++pos;
00131         }
00132     }
00133     return nr;
00134 }
00135 
00136 
00137 
00138 
00139 
00140 
00141 
00142 
00143 /*********************************************************************
00144                         C O N S T R U C T O R
00145  *********************************************************************/
00146 
00147 
00148 DOCConverter::DOCConverter(QObject *parent, const char *name):QObject(parent,name) {
00149     FUNCTIONSETUP;
00150     docdb=0L;
00151     eSortBookmarks=eSortNone;
00152     fBookmarks.setAutoDelete( TRUE );
00153 }
00154 
00155 
00156 
00157 DOCConverter::~DOCConverter() {
00158     FUNCTIONSETUP;
00159 }
00160 
00161 
00162 
00163 
00164 
00165 /*********************************************************************
00166                 S Y N C   S T R U C T U R E
00167  *********************************************************************/
00168 
00169 
00170 
00171 void DOCConverter::setTXTpath(QString path, QString file) {
00172     QDir dr(path);
00173     QFileInfo pth(dr, file);
00174     if (!file.isEmpty())
00175          txtfilename = pth.absFilePath();
00176 }
00177 
00178 
00179 
00180 void DOCConverter::setTXTpath(QString filename) {
00181     if (!filename.isEmpty()) txtfilename = filename;
00182 }
00183 
00184 
00185 
00186 void DOCConverter::setPDB(PilotDatabase * dbi) {
00187     if (dbi) docdb = dbi;
00188 }
00189 
00190 
00191 
00192 QString DOCConverter::readText() {
00193     FUNCTIONSETUP;
00194     if (txtfilename.isEmpty()) return QString();
00195     QFile docfile(txtfilename);
00196     if (!docfile.open(IO_ReadOnly))
00197     {
00198         emit logError(i18n("Unable to open text file %1 for reading.").arg(txtfilename));
00199         return QString();
00200     }
00201 
00202     QTextStream docstream(&docfile);
00203 
00204     QString doc = docstream.read();
00205     docfile.close();
00206     return doc;
00207 }
00208 
00209 
00210 
00211 int DOCConverter::findBmkEndtags(QString &text, bmkList&fBmks) {
00212     FUNCTIONSETUP;
00213     // Start from the end of the text
00214     int pos = text.length() - 1, nr=0;
00215     bool doSearch=true;
00216     while (pos >= 0/* && doSearch*/) {
00217         DEBUGKPILOT<<"Current character is \'"<<text[pos].latin1()<<"\'"<<endl;
00218         // skip whitespace until we reach a >
00219         while (text[pos].isSpace() && pos >= 0) {
00220             DEBUGKPILOT<<"Skipping whitespaces at the end of the file"<<endl;
00221             pos--;
00222         }
00223         // every other character than a > is assumed to belong to the text, so there are no more bookmarks.
00224         if (pos < 0 || text[pos] != '>') {
00225             DEBUGKPILOT<<"Current character \'"<<text[pos].latin1()<<"\' at position "<<pos<<" is not and ending >. Finish searching for bookmarks."<<endl;
00226 
00227             pos=-1;
00228             break;
00229         } else {
00230             int endpos = pos;
00231             doSearch=true;
00232             DEBUGKPILOT<<"Found the ending >, now looking for the opening <"<<endl;
00233 
00234             // Search for the opening <. There must not be a newline in the bookmark text.
00235             while (doSearch && pos > 0) {
00236 //              DEBUGKPILOT<<"pos="<<pos<<", char="<<text[pos].latin1()<<endl;
00237                 pos--;
00238                 if (text[pos] == '\n') {
00239                     DEBUGKPILOT<<"Found carriage return at position "<<pos<<" inside the bookmark text, assuming this is not a bookmark, and the text ends in a >"<<endl;
00240                     doSearch = false;
00241                     pos = -1;
00242                     break;
00243                 }
00244                 if (text[pos] == '<') {
00245                     fBmks.append(new docMatchBookmark(text.mid(pos + 1, endpos - pos - 1)));
00246                     ++nr;
00247                     DEBUGKPILOT<<"Found opening < at position "<<pos<<", bookmarktext ="<<text.mid(pos+1, endpos-pos-1)<<endl;
00248                     text.remove(pos, text.length());
00249                     pos--;
00250                     doSearch = false;
00251                 }
00252             }
00253         }
00254         DEBUGKPILOT<<"Finished processing the next bookmark, current position: "<<pos<<endl;
00255     }
00256     return nr;
00257 }
00258 
00259 int DOCConverter::findBmkInline(QString &text, bmkList &fBmks) {
00260     FUNCTIONSETUP;
00261 //  bmkList res;
00262     int nr=0;
00263     QRegExp rx(CSL1("<\\*(.*)\\*>"));
00264 
00265     rx.setMinimal(TRUE);
00266     int pos = 0;
00267     while (pos >= 0) {
00268         pos = rx.search(text, pos);
00269         if (pos >= 0) {
00270             fBmks.append(new docBookmark(rx.cap(1), pos+1));
00271             ++nr;
00272             text = text.remove(pos, rx.matchedLength());
00273         }
00274     }
00275     return nr;
00276 }
00277 
00278 int DOCConverter::findBmkFile(QString &, bmkList &fBmks) {
00279     FUNCTIONSETUP;
00280     int nr=0;
00281 
00282     QString bmkfilename = txtfilename;
00283     if (bmkfilename.endsWith(CSL1(".txt"))){
00284         bmkfilename.remove(bmkfilename.length()-4, 4);
00285     }
00286     QString oldbmkfilename=bmkfilename;
00287     bmkfilename+=CSL1(BMK_SUFFIX);
00288     QFile bmkfile(bmkfilename);
00289     if (!bmkfile.open(IO_ReadOnly))     {
00290         bmkfilename=oldbmkfilename+CSL1(PDBBMK_SUFFIX);
00291         bmkfile.setName(bmkfilename);
00292         if (!bmkfile.open(IO_ReadOnly)) {
00293             DEBUGKPILOT<<"Unable to open bookmarks file "<<bmkfilename<<" for reading the bookmarks of "<<docdb ->dbPathName()<<endl;
00294             return 0;
00295         }
00296     }
00297 
00298     DEBUGKPILOT<<"Bookmark file: "<<bmkfilename<<endl;
00299 
00300     QTextStream bmkstream(&bmkfile);
00301     QString line;
00302     while ( !(line=bmkstream.readLine()).isEmpty() ) {
00303         if (!line.isEmpty() && !line.startsWith(CSL1("#")) ) {
00304             QStringList bmkinfo=QStringList::split(CSL1(","), line);
00305             int fieldnr=bmkinfo.count();
00306             // We use the same syntax for the entries as MakeDocJ bookmark files:
00307             //   <bookmark>,<string-to-search>,<bookmark-name-string>,<starting-bookmark>,<ending-bookmark>
00308             // For an explanation see: http://home.kc.rr.com/krzysztow/PalmPilot/MakeDocJ/index.html
00309             if (fieldnr>0){
00310                 DEBUGKPILOT<<"Working on bookmark \""<<line<<"\""<<endl;
00311                 docMatchBookmark*bmk=0L;
00312                 QString bookmark=bmkinfo[0];
00313                 bool ok;
00314                 int pos=bookmark.toInt(&ok);
00315                 if (ok) {
00316                     if (fieldnr>1) {
00317                         QString name(bmkinfo[1]);
00318                         DEBUGKPILOT<<"Bookmark \""<<name<<"\" set at position "<<pos<<endl;
00319                         fBmks.append(new docBookmark(name, pos));
00320                     }
00321                 } else if (bookmark==CSL1("-") || bookmark==CSL1("+")) {
00322                     if (fieldnr>1) {
00323                         QString patt(bmkinfo[1]);
00324                         QString name(patt);
00325                         if (fieldnr>2) {
00326                             int cap=bmkinfo[2].toInt(&ok);
00327                             if (ok) {
00328                                 bmk=new docRegExpBookmark(patt, cap);
00329                             } else {
00330                                 name=bmkinfo[2];
00331                                 bmk=new docRegExpBookmark(patt, name);
00332                             }
00333                         } else{
00334                             bmk=new docRegExpBookmark(patt, name);
00335                         }
00336                         // The third entry in the line (optional) denotes the index of a capture subexpression (if an integer) or the bookmark text as regexp (if a string)
00337                         DEBUGKPILOT<<"RegExp Bookmark, pattern="<<patt<<", name="<<name<<endl;
00338                         if (bmk) {
00339                             if (bookmark==CSL1("-")) {
00340                                 bmk->from=1;
00341                                 bmk->to=1;
00342                             } else {
00343                                 if (fieldnr>3) {
00344                                     bool ok;
00345                                     int tmp=bmkinfo[3].toInt(&ok);
00346                                     if (ok) bmk->from=tmp;
00347                                     if (fieldnr>4) {
00348                                         tmp=bmkinfo[4].toInt(&ok);
00349                                         if (ok) bmk->to=tmp;
00350                                     }
00351                                 }
00352                             }
00353                             fBmks.append(bmk);
00354                             bmk=0L;
00355                         } else {
00356                             DEBUGKPILOT<<"Could not allocate bookmark "<<name<<endl;
00357                         }
00358                     } else {
00359                         DEBUGKPILOT<<"RegExp bookmark found with no other information (no bookmark pattern nor name)"<<endl;
00360                     }
00361                 } else {
00362                     QString pattern(bookmark);
00363                     if (fieldnr>1) pattern=bmkinfo[1];
00364                     if (fieldnr>2) bookmark=bmkinfo[2];
00365                     DEBUGKPILOT<<"RegExp Bookmark, pattern="<<pattern<<", name="<<bookmark<<endl;
00366                     bmk=new docRegExpBookmark(pattern, bookmark);
00367                     if (bmk) {
00368                         bmk->from=1;
00369                         bmk->to=1;
00370                         fBmks.append(bmk);
00371                     }
00372                 }
00373             } // fieldnr>0
00374         } // !line.isEmpty()
00375     } // while
00376     return nr;
00377 }
00378 
00379 bool DOCConverter::convertTXTtoPDB() {
00380     FUNCTIONSETUP;
00381 
00382     if (!docdb) {
00383         emit logError(i18n("Unable to open Database for writing"));
00384         return false;
00385     }
00386 
00387     QString text = readText();
00388 
00389     if (fBmkTypes & eBmkEndtags) {
00390         findBmkEndtags(text, fBookmarks);
00391     }   // end: EndTag Bookmarks
00392 
00393 
00394     // Search for all tags <* Bookmark text *> in the text. We have to delete them immediately, otherwise the later bookmarks will be off.
00395     if (fBmkTypes & eBmkInline) {
00396         findBmkInline(text, fBookmarks);
00397     }    // end: Inline Bookmarks
00398 
00399 
00400     // Read in regular expressions and positions from an external file (doc-filename with extension .bmk)
00401     if (fBmkTypes & eBmkFile)
00402     {
00403         findBmkFile(text, fBookmarks);
00404     }
00405 
00406     // Process the bookmarks: find the occurrences of the regexps, and sort them if requested:
00407     bmkSortedList pdbBookmarks;
00408     pdbBookmarks.setAutoDelete(TRUE);
00409     docBookmark*bmk;
00410     for (bmk = fBookmarks.first(); bmk; bmk = fBookmarks.next())
00411     {
00412         bmk->findMatches(text, pdbBookmarks);
00413     }
00414 
00415     switch (eSortBookmarks)
00416     {
00417         case eSortName:
00418             docBookmark::compare_pos=false;
00419 //          qHeapSort(pdbBookmarks);
00420             pdbBookmarks.sort();
00421             break;
00422         case eSortPos:
00423             docBookmark::compare_pos=true;
00424             pdbBookmarks.sort();
00425             break;
00426         case eSortNone:
00427         default:
00428             break;
00429     }
00430 
00431 #ifdef DEBUG
00432     DEBUGKPILOT << "Bookmarks: "<<endl;
00433     for (bmk = pdbBookmarks.first(); bmk; bmk = pdbBookmarks.next())
00434     {
00435         DEBUGKPILOT<<bmk->bmkName.left(20)<<" at position "<<bmk->position<<endl;
00436     }
00437 #endif
00438 
00439     if (!docdb->isOpen()) {
00440         emit logError(i18n("Unable to open palm doc database %1").arg(docdb->dbPathName()) );
00441         return false;
00442     }
00443 
00444     // Clean the whole database, otherwise the records would be just appended!
00445     docdb->deleteRecord(0, true);
00446 
00447     // Header record for the doc file format
00448     PilotDOCHead docHead;
00449     docHead.position=0;
00450     docHead.recordSize=4096;
00451     docHead.spare=0;
00452     docHead.storyLen=text.length();
00453     docHead.version=compress?DOC_COMPRESSED:DOC_UNCOMPRESSED;
00454     docHead.numRecords=(int)( (text.length()-1)/docHead.recordSize)+1;
00455     PilotRecord*rec=docHead.pack();
00456     docdb->writeRecord(rec);
00457     KPILOT_DELETE(rec);
00458 
00459     DEBUGKPILOT << "Write header record: length="<<text.length()<<", compress="<<compress<<endl;
00460 
00461     // First compress the text, then write out the bookmarks and - if existing - also the annotations
00462     int len=text.length();
00463     int start=0,reclen=0;
00464     int recnum=0;
00465     while (start<len)
00466     {
00467         reclen=min(len-start, PilotDOCEntry::TEXT_SIZE);
00468         DEBUGKPILOT << "Record #"<<recnum<<", reclen="<<reclen<<", compress="<<compress<<endl;
00469 
00470         PilotDOCEntry recText;
00471 //      recText.setText(text.mid(start, reclen), reclen);
00472         recText.setText(text.mid(start, reclen));
00473 //      if (compress)
00474         recText.setCompress(compress);
00475         PilotRecord*textRec=recText.pack();
00476         docdb->writeRecord(textRec);
00477         ++recnum;
00478         start+=reclen;
00479         KPILOT_DELETE(textRec);
00480     }
00481 
00482     recnum=0;
00483     // Finally, write out the bookmarks
00484     for (bmk = pdbBookmarks.first(); bmk; bmk = pdbBookmarks.next())
00485 //  for (bmkList::const_iterator it=pdbBookmarks.begin(); it!=pdbBookmarks.end(); ++it)
00486     {
00487         ++recnum;
00488         DEBUGKPILOT << "Bookmark #"<<recnum<<", Name="<<bmk->bmkName.left(20)<<", Position="<<bmk->position<<endl;
00489 
00490         PilotDOCBookmark bmkEntry;
00491         bmkEntry.pos=bmk->position;
00492         strncpy(&bmkEntry.bookmarkName[0], bmk->bmkName.latin1(), 16);
00493         PilotRecord*bmkRecord=bmkEntry.pack();
00494         docdb->writeRecord(bmkRecord);
00495         KPILOT_DELETE(bmkRecord);
00496     }
00497 
00498     pdbBookmarks.clear();
00499     fBookmarks.clear();
00500 
00501     return true;
00502 }
00503 
00504 
00505 
00506 bool DOCConverter::convertPDBtoTXT()
00507 {
00508     FUNCTIONSETUP;
00509     if (txtfilename.isEmpty()) {
00510         emit logError(i18n("No filename set for the conversion"));
00511         return false;
00512     }
00513 
00514     if (!docdb) {
00515         emit logError(i18n("Unable to open Database for reading"));
00516         return false;
00517     }
00518 
00519     // The first record of the db is the document header containing information about the doc db
00520     PilotRecord*headerRec = docdb->readRecordByIndex(0);
00521     if (!headerRec)
00522     {
00523         emit logError(i18n("Unable to read database header for database %1.").arg(docdb->dbPathName()));
00524         KPILOT_DELETE(docdb);
00525         return false;
00526     }
00527     PilotDOCHead header(headerRec);
00528     KPILOT_DELETE(headerRec);
00529 
00530     DEBUGKPILOT<<"Database "<<docdb->dbPathName()<<" has "<<header.numRecords<<" text records, "<<endl
00531         <<" total number of records: "<<docdb->recordCount()<<endl
00532         <<" position="<<header.position<<endl
00533         <<" recordSize="<<header.recordSize<<endl
00534         <<" spare="<<header.spare<<endl
00535         <<" storyLen="<<header.storyLen<<endl
00536 //      <<" textRecordSize="<<header.textRecordSize<<endl
00537         <<" version="<<header.version<<endl;
00538 
00539     // next come the header.numRecords real document records (might be compressed, see the version flag in the header)
00540     QFile docfile(txtfilename);
00541     if (!docfile.open(IO_WriteOnly))
00542     {
00543         emit logError(i18n("Unable to open output file %1.").arg(txtfilename));
00544         KPILOT_DELETE(docdb);
00545         return false;
00546     }
00547     QString doctext;
00548     for (int i=1; i<header.numRecords+1; ++i)
00549     {
00550         PilotRecord*rec=docdb->readRecordByIndex(i);
00551         if (rec)
00552         {
00553             PilotDOCEntry recText(rec, header.version==DOC_COMPRESSED);
00554             doctext.append(recText.getText());
00555             DEBUGKPILOT<<"Record "<<i<<endl;
00556             KPILOT_DELETE(rec);
00557         } else {
00558             emit logMessage(i18n("Could not read text record #%1 from Database %2").arg(i).arg(docdb->dbPathName()));
00559         }
00560     }
00561 
00562     // After the document records possibly come a few bookmark records, so read them in and put them in a separate bookmark file.
00563     // for the ztxt conduit there might be annotations after the bookmarks, so the upper bound needs to be adapted.
00564     int upperBmkRec=docdb->recordCount();
00565     bmkSortedList bmks;
00566     bmks.setAutoDelete(TRUE);
00567     for (int i=header.numRecords+1; i<upperBmkRec; ++i)
00568     {
00569         PilotRecord*rec=docdb->readRecordByIndex(i);
00570         if (rec)
00571         {
00572             PilotDOCBookmark bookie(rec);
00573             docBookmark*bmk=new docBookmark(QString::fromLatin1(bookie.bookmarkName), bookie.pos);
00574             bmks.append(bmk);
00575             KPILOT_DELETE(rec);
00576         } else {
00577             emit logMessage(i18n("Could not read bookmark record #%1 from Database %2").arg(i).arg(docdb->dbPathName()));
00578         }
00579     }
00580     // TODO: Sort the list of bookmarks according to their position
00581     docBookmark::compare_pos=true;
00582     bmks.sort();
00583 
00584     if ((fBmkTypes & eBmkFile) && (bmks.count()>0))
00585     {
00586         QString bmkfilename = docfile.name();
00587         if (bmkfilename.endsWith(CSL1(".txt"))){
00588             bmkfilename.remove(bmkfilename.length()-4, 4);
00589         }
00590         bmkfilename+=CSL1(PDBBMK_SUFFIX);
00591         QFile bmkfile(bmkfilename);
00592         if (!bmkfile.open(IO_WriteOnly))
00593         {
00594             emit logError(i18n("Unable to open file %1 for the bookmarks of %2.")
00595                 .arg(bmkfilename).arg(docdb ->dbPathName()));
00596         }
00597         else
00598         {
00599             DEBUGKPILOT<<"Writing "<<upperBmkRec-header.numRecords<<
00600                 "("<<upperBmkRec<<") bookmarks to file "<<bmkfilename<<endl;
00601             QTextStream bmkstream(&bmkfile);
00602             for (docBookmark*bmk=bmks.first(); bmk; bmk=bmks.next())
00603             {
00604                 bmkstream<<bmk->position<<", "<<bmk->bmkName<<endl;
00605             }
00606             //bmkstream.close();
00607             bmkfile.close();
00608         }
00609     }
00610     if (fBmkTypes & eBmkInline)
00611     {
00612         for (docBookmark*bmk=bmks.last(); bmk; bmk=bmks.prev())
00613         {
00614             doctext.insert(bmk->position, QString(CSL1("<*") +
00615                 bmk->bmkName +
00616                 CSL1("*>")));
00617         }
00618     }
00619 
00620     // Finally, write the actual text out to the file.
00621     QTextStream docstream(&docfile);
00622     docstream<<doctext;
00623     //docstream.close();
00624     docfile.close();
00625     docdb->cleanup();
00626     // reset all records to unchanged. I don't know if this is really such a wise idea?
00627     docdb->resetSyncFlags();
00628     return true;
00629 }
00630 
00631