Bookmarks.cpp
上传用户:hmc_gdtv
上传日期:2013-08-04
资源大小:798k
文件大小:18k
源码类别:

Windows Mobile

开发平台:

Visual C++

  1. /*
  2.  * Copyright (c) 2001,2002,2003 Mike Matsnev.  All Rights Reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice immediately at the beginning of the file, without modification,
  10.  *    this list of conditions, and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  * 3. Absolutely no warranty of function or purpose is made by the author
  15.  *    Mike Matsnev.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  * 
  28.  * $Id: Bookmarks.cpp,v 1.42.2.6 2005/06/18 19:44:12 mike Exp $
  29.  * 
  30.  */
  31. #include <afxwin.h>
  32. #include <afxtempl.h>
  33. #include "config.h"
  34. #include "ptr.h"
  35. #include "FilePos.h"
  36. #include "Bookmarks.h"
  37. #include "TextViewNG.h"
  38. #include "Unicode.h"
  39. #include "TextFile.h"
  40. struct Goodch {
  41.   static bool isgoodch(TCHAR ch) { return (ch>=_T('0') && ch<=_T('9')) || (ch>=_T('a') && ch<=_T('z')) || (ch>=_T('A') && ch<=_T('Z')) || ch==_T('.') || ch==_T('-') || ch==_T('_') || ch==_T(' '); }
  42. };
  43. struct Goodch2 {
  44.   static bool isgoodch(TCHAR ch) { return ch!=_T('%') && ch!=_T('/') && ch!=_T('\'); }
  45. };
  46. struct Goodch3 {
  47.   static bool isgoodch(TCHAR ch) { return ch!=_T('%') && ch!=_T('r') && ch!=_T('n'); }
  48. };
  49. template<class T>
  50. static CString param_escape(const CString& str) {
  51.   int   rlen;
  52.   int   i;
  53.   CString ret;
  54.   TCHAR   *cp;
  55.   for (i=rlen=0;i<str.GetLength();++i)
  56.     if (T::isgoodch(str[i]))
  57.       ++rlen;
  58.     else
  59.       rlen+=5;
  60.   cp=ret.GetBuffer(rlen);
  61.   for (i=0;i<str.GetLength();++i)
  62.     if (T::isgoodch(str[i]))
  63.       *cp++=str[i];
  64.     else {
  65.       _stprintf(cp,_T("%%%04X"),((unsigned)str[i])&0xffff);
  66.       cp+=5;
  67.     }
  68.   ret.ReleaseBuffer(rlen);
  69.   return ret;
  70. }
  71. #define escape(x)   param_escape<Goodch>(x)
  72. #define escape2(x)  param_escape<Goodch2>(x)
  73. #define escape3(x)  param_escape<Goodch3>(x)
  74. static CString unescape(const CString& str) {
  75.   int   rlen;
  76.   int   i,j,n;
  77.   CString ret;
  78.   TCHAR   *cp;
  79.   for (i=rlen=0;i<str.GetLength();++i) {
  80.     if (str[i]==_T('%')) {
  81.       if (i+4<str.GetLength())
  82. i+=4;
  83.       else
  84. break;
  85.     }
  86.     ++rlen;
  87.   }
  88.   cp=ret.GetBuffer(rlen);
  89.   for (i=0;i<str.GetLength();++i) {
  90.     if (str[i]==_T('%')) {
  91.       if (i+4>=str.GetLength())
  92. break;
  93.       for (j=1+i,n=0;j<5+i;++j) {
  94. n<<=4;
  95. if (str[j]>=_T('0') && str[j]<=_T('9'))
  96.   n+=str[j]-_T('0');
  97. else if (str[j]>=_T('A') && str[j]<=_T('F'))
  98.   n+=str[j]-_T('A')+10;
  99. else if (str[j]>=_T('a') && str[j]<=_T('f'))
  100.   n+=str[j]-_T('a')+10;
  101.       }
  102.       *cp++=n==0 ? _T(' ') : n;
  103.       i+=4;
  104.     } else
  105.       *cp++=str[i];
  106.   }
  107.   ret.ReleaseBuffer(rlen);
  108.   return ret;
  109. }
  110. Bookmarks::Bookmarks(const CString& filename) : m_filename(filename),
  111.   m_shortname(filename), m_changed(true), m_topbmk(0), m_ubmk(0)
  112. {
  113.   // extract filename
  114.   int last=max(m_shortname.ReverseFind(_T('\')),m_shortname.ReverseFind(_T('/')));
  115.   m_shortname.Delete(0,last+1);
  116.   // find info in registry
  117.   CString info=AfxGetApp()->GetProfileString(_T("Bookmarks"),escape2(m_shortname));
  118.   DWORD dummy;
  119.   if (!info.GetLength() || _stscanf(info,_T("%d,%d,%u,%u"),
  120.     &m_format,&m_encoding,&dummy,&dummy)!=4)
  121.   {
  122.     m_format=-1;
  123.     m_encoding=CTVApp::GetInt(_T("DefEncoding"),-1);
  124.     m_startpos.off=m_startpos.para=0;
  125.   }
  126. }
  127. Bookmarks::~Bookmarks() {
  128.   SaveInfo();
  129. }
  130. CString Bookmarks::Text(int idx,TextFile *parser) {
  131.   if (m_bmk[idx].flags & BMK) {
  132.     // return changed text if possible
  133.     if (m_bmk[idx].flags & BMCHG && m_bmk[idx].tmp)
  134.       return *m_bmk[idx].tmp;
  135.     return m_bmk[idx].text;
  136.   }
  137.   if (m_bmk[idx].text.GetLength()>0)
  138.     return m_bmk[idx].text;
  139.   FilePos p(m_bmk[idx].text_ref);
  140.   if (p.off==0 || !parser)
  141.     return CString();
  142.   // ok, here we extract the bookmark from the file
  143.   CString ret;
  144.   int   ps=p.para;
  145.   int   pe=p.para+p.off;
  146.   while (ps<pe) {
  147.     if (p.para!=ps)
  148.       ret+=_T(" ");
  149.     ret+=Unicode::ToCS(parser->GetParagraph(p.docid,ps).str);
  150.     ++ps;
  151.   }
  152.   return ret;
  153. }
  154. int  Bookmarks::AddImp(int para_start,int para_count,int docid,const CString *text,
  155.        FilePos pos,DWORD flags,int level)
  156. {
  157.   m_changed=true;
  158.   BE be;
  159.   if (text)
  160.     be.text=*text;
  161.   be.ref=pos;
  162.   be.text_ref.para=para_start;
  163.   be.text_ref.off=para_count;
  164.   be.text_ref.docid=docid;
  165.   ASSERT(docid>=0);
  166.   be.flags=flags;
  167.   be.level=level;
  168.   int index=BFind(pos,SNEXTICH);
  169.   if (index>=m_bmk.GetSize())
  170.     m_bmk.Add(be);
  171.   else
  172.     m_bmk.InsertAt(index,be);
  173.   if (flags&BMK)
  174.     ++m_topbmk,++m_ubmk;
  175.   else if (level==0)
  176.     ++m_topbmk;
  177.   return index;
  178. }
  179. void  Bookmarks::Remove(int index) {
  180.   Check(index);
  181.   if (m_bmk[index].flags&BMK)
  182.     m_bmk[index].flags|=BMDEL;
  183. }
  184. void  Bookmarks::Change(int index,const CString& text) {
  185.   Check(index);
  186.   if (m_bmk[index].flags&BMK) {
  187.     if (m_bmk[index].flags&BMCHG)
  188.       *m_bmk[index].tmp=text;
  189.     else
  190.       m_bmk[index].tmp=new CString(text);
  191.     m_bmk[index].flags|=BMCHG;
  192.   }
  193. }
  194. int  Bookmarks::Commit(int cidx) {
  195.   for (int i=0;i<m_bmk.GetSize();) {
  196.     if (m_bmk[i].flags&BMCHG) {
  197.       m_changed = true;
  198.       m_bmk[i].text=*m_bmk[i].tmp;
  199.       delete m_bmk[i].tmp;
  200.       m_bmk[i].flags&=~BMCHG;
  201.       m_bmk[i].flags|=BMNEW;
  202.     }
  203.     if (m_bmk[i].flags&BMDEL) {
  204.       m_changed = true;
  205.       m_bmk.RemoveAt(i);
  206.       if (cidx>i)
  207. --cidx;
  208.       --m_topbmk;
  209.       --m_ubmk;
  210.     } else
  211.       ++i;
  212.   }
  213.   return cidx;
  214. }
  215. void  Bookmarks::Rollback() {
  216.   for (int i=0;i<m_bmk.GetSize();++i) {
  217.     if (m_bmk[i].flags&BMCHG)
  218.       delete m_bmk[i].tmp;
  219.     m_bmk[i].flags&=~(BMCHG|BMDEL);
  220.   }
  221. }
  222. void  Bookmarks::SaveToRegistry() {
  223.   if (m_shortname==_T("NUL"))
  224.     return;
  225.   HKEY   hBmk=AfxGetApp()->GetSectionKey(_T("Bookmarks"));
  226.   if (!hBmk)
  227.     return;
  228.   CString sect(escape2(m_shortname));
  229.   RegDeleteKey(hBmk,sect);
  230.   HKEY res;
  231.   DWORD disp;
  232.   CString name,value;
  233.   if (RegCreateKeyEx(hBmk,sect,0,REG_NONE,0,HR_REG_PERM,NULL,&res,&disp)==ERROR_SUCCESS) {
  234.     value.Format(_T("%d,%d,%d"),m_startpos.para,m_startpos.off,m_startpos.docid);
  235.     RegSetValueEx(res,NULL,0,REG_SZ,(LPBYTE)(const TCHAR *)value,
  236.       (value.GetLength()+1)*sizeof(TCHAR));
  237.     int n=0;
  238.     for (int i=0;i<m_bmk.GetSize();++i) {
  239.       if (!(m_bmk[i].flags&BMK))
  240. continue;
  241.       name.Format(_T("%d"),n);
  242.       value.Format(_T("%d,%d,%d,%s"),m_bmk[i].ref.para,m_bmk[i].ref.off,
  243. m_bmk[i].ref.docid,(const TCHAR *)m_bmk[i].text);
  244.       RegSetValueEx(res,name,0,REG_SZ,(LPBYTE)(const TCHAR *)value,
  245. (value.GetLength()+1)*sizeof(TCHAR));
  246.       ++n;
  247.     }
  248.     RegCloseKey(res);
  249.   }
  250.   RegCloseKey(hBmk);
  251. }
  252. bool  Bookmarks::SaveInfo() {
  253.   if (m_changed) {
  254.     if (m_shortname==_T("NUL"))
  255.       return false;
  256.     SYSTEMTIME tm;
  257.     FILETIME ftm;
  258.     GetLocalTime(&tm);
  259.     SystemTimeToFileTime(&tm,&ftm);
  260.     CString info;
  261.     info.Format(_T("%d,%d,%u,%u,%s"),m_format,m_encoding,
  262.       ftm.dwLowDateTime,ftm.dwHighDateTime,m_filename);
  263.     AfxGetApp()->WriteProfileString(_T("Bookmarks"),escape2(m_shortname),info);
  264.     m_changed=false;
  265.     return true;
  266.   }
  267.   return false;
  268. }
  269. void  Bookmarks::LoadFromRegistry() {
  270.   HKEY   hBmk=AfxGetApp()->GetSectionKey(_T("Bookmarks"));
  271.   if (!hBmk)
  272.     return;
  273.   CString sect(escape2(m_shortname));
  274.   HKEY res;
  275.   CString name,value;
  276.   if (RegOpenKeyEx(hBmk,sect,0,HR_REG_PERM,&res)==ERROR_SUCCESS) {
  277.     TCHAR     buf[4096];
  278.     DWORD     type,len=sizeof(buf);
  279.     if (RegQueryValueEx(res,NULL,0,&type,(LPBYTE)buf,&len)==ERROR_SUCCESS && type==REG_SZ) {
  280.       FilePos pp;
  281.       if (_stscanf(buf,_T("%d,%d,%d"),&pp.para,&pp.off,&pp.docid)>=2)
  282. SetStartPos(pp);
  283.     }
  284.     for (int i=0;;++i) {
  285.       name.Format(_T("%d"),i);
  286.       len=sizeof(buf);
  287.       if (RegQueryValueEx(res,name,0,&type,(LPBYTE)buf,&len)==ERROR_SUCCESS && type==REG_SZ) {
  288. FilePos pp;
  289. TCHAR *cp=value.GetBuffer(sizeof(buf)/sizeof(TCHAR));
  290. if (_stscanf(buf,_T("%d,%d,%d,%[^01]"),&pp.para,&pp.off,&pp.docid,cp)==4 ||
  291.     _stscanf(buf,_T("%d,%d,%[^01]"),&pp.para,&pp.off,cp)==3)
  292. {
  293.   value.ReleaseBuffer();
  294.   Add(value,pp);
  295. } else
  296.   value.ReleaseBuffer(0);
  297.       } else
  298. break;
  299.     }
  300.     RegCloseKey(res);
  301.   }
  302.   RegCloseKey(hBmk);
  303. }
  304. CString Bookmarks::find_last_file() {
  305.   HKEY     hKey=AfxGetApp()->GetSectionKey(_T("Bookmarks"));
  306.   if (!hKey)
  307.     return CString();
  308.   CString   filename,tmp;
  309.   FILETIME  tt;
  310.   tt.dwLowDateTime=tt.dwHighDateTime=0;
  311.   for (DWORD index=0;;++index) {
  312.     TCHAR   name[1024];
  313.     DWORD   namelen=sizeof(name)/sizeof(TCHAR);
  314.     TCHAR   value[4096];
  315.     DWORD   valuelen=sizeof(value);
  316.     DWORD   type;
  317.     if (RegEnumValue(hKey,index,name,&namelen,NULL,&type,(LPBYTE)value,&valuelen)!=ERROR_SUCCESS)
  318.       break;
  319.     if (type!=REG_SZ)
  320.       continue;
  321.     int     dummy;
  322.     TCHAR   *cp=tmp.GetBuffer(valuelen);
  323.     FILETIME  tm;
  324.     if (_stscanf(value,_T("%d,%d,%u,%u,%[^01]"),&dummy,&dummy,&tm.dwLowDateTime,&tm.dwHighDateTime,cp)!=5) {
  325.       tmp.ReleaseBuffer(0);
  326.       continue;
  327.     }
  328.     tmp.ReleaseBuffer();
  329.     if (tm.dwHighDateTime>tt.dwHighDateTime ||
  330. (tm.dwHighDateTime==tt.dwHighDateTime && tm.dwLowDateTime>tt.dwLowDateTime))
  331.     {
  332.       tt=tm;
  333.       filename=tmp;
  334.     }
  335.   }
  336.   RegCloseKey(hKey);
  337.   return filename;
  338. }
  339. struct Item {
  340.   TCHAR       *name;
  341.   FILETIME    time;
  342. };
  343. static int __cdecl itemcmp(const void *v1,const void *v2) {
  344.   const Item *i1=(const Item *)v1;
  345.   const Item *i2=(const Item *)v2;
  346.   if (i1->time.dwHighDateTime<i2->time.dwHighDateTime)
  347.     return 1;
  348.   if (i1->time.dwHighDateTime>i2->time.dwHighDateTime)
  349.     return -1;
  350.   if (i1->time.dwLowDateTime<i2->time.dwLowDateTime)
  351.     return 1;
  352.   if (i1->time.dwLowDateTime>i2->time.dwLowDateTime)
  353.     return -1;
  354.   return 0;
  355. }
  356. void Bookmarks::CleanupRegistry(int max_count) {
  357.   HKEY     hKey=AfxGetApp()->GetSectionKey(_T("Bookmarks"));
  358.   if (!hKey)
  359.     return;
  360.   CArray<Item,Item&>  ilist;
  361.   Item       ii;
  362.   for (DWORD index=0;;++index) {
  363.     TCHAR   name[1024];
  364.     DWORD   namelen=sizeof(name)/sizeof(TCHAR);
  365.     TCHAR   value[4096];
  366.     DWORD   valuelen=sizeof(value);
  367.     DWORD   type;
  368.     if (RegEnumValue(hKey,index,name,&namelen,NULL,&type,(LPBYTE)value,&valuelen)!=ERROR_SUCCESS)
  369.       break;
  370.     if (type!=REG_SZ)
  371.       continue;
  372.     int     dummy;
  373.     if (_stscanf(value,_T("%d,%d,%u,%u,"),&dummy,&dummy,
  374.       &ii.time.dwLowDateTime,&ii.time.dwHighDateTime)!=4)
  375.       continue;
  376.     ii.name=_tcsdup(name);
  377.     if (ii.name)
  378.       ilist.Add(ii);
  379.   }
  380.   if (ilist.GetSize()>max_count) {
  381.     qsort(ilist.GetData(),ilist.GetSize(),sizeof(Item),itemcmp);
  382.     for (int kk=max_count;kk<ilist.GetSize();++kk) {
  383.       RegDeleteKey(hKey,ilist[kk].name);
  384.       RegDeleteValue(hKey,ilist[kk].name);
  385.     }
  386.   }
  387.   RegCloseKey(hKey);
  388.   for (int jj=0;jj<ilist.GetSize();++jj)
  389.     free(ilist[jj].name);
  390. }
  391. void Bookmarks::get_recent_files(CStringArray& fl,int num,FILETIME& toptime) {
  392.   HKEY     hKey=AfxGetApp()->GetSectionKey(_T("Bookmarks"));
  393.   if (!hKey)
  394.     return;
  395.   CArray<Item,Item&>  ilist;
  396.   Item       ii;
  397.   CString       tmp;
  398.   for (DWORD index=0;;++index) {
  399.     TCHAR   name[1024];
  400.     DWORD   namelen=sizeof(name)/sizeof(TCHAR);
  401.     TCHAR   value[4096];
  402.     DWORD   valuelen=sizeof(value);
  403.     DWORD   type;
  404.     if (RegEnumValue(hKey,index,name,&namelen,NULL,&type,(LPBYTE)value,&valuelen)!=ERROR_SUCCESS)
  405.       break;
  406.     if (type!=REG_SZ)
  407.       continue;
  408.     int     dummy;
  409.     TCHAR   *cp=tmp.GetBuffer(valuelen);
  410.     if (_stscanf(value,_T("%d,%d,%u,%u,%[^01]"),&dummy,&dummy,
  411.       &ii.time.dwLowDateTime,&ii.time.dwHighDateTime,cp)!=5)
  412.     {
  413.       tmp.ReleaseBuffer(0);
  414.       continue;
  415.     }
  416.     tmp.ReleaseBuffer();
  417.     ii.name=_tcsdup(tmp);
  418.     if (ii.name)
  419.       ilist.Add(ii);
  420.   }
  421.   qsort(ilist.GetData(),ilist.GetSize(),sizeof(Item),itemcmp);
  422.   for (int kk=0;kk<ilist.GetSize() && kk<num;++kk)
  423.     fl.Add(ilist[kk].name);
  424.   if (ilist.GetSize()>0)
  425.     toptime=ilist[0].time;
  426.   RegCloseKey(hKey);
  427.   for (int jj=0;jj<ilist.GetSize();++jj)
  428.     free(ilist[jj].name);
  429. }
  430. int   Bookmarks::BFind(FilePos p,int type) {
  431.   int low=0,high=m_bmk.GetSize()-1;
  432.   while (low<=high) {
  433.     int mid=(low+high)>>1;
  434.     if (p<m_bmk[mid].ref)
  435.       high=mid-1;
  436.     else if (m_bmk[mid].ref<p)
  437.       low=mid+1;
  438.     else {
  439.       switch (type) {
  440.       case SPREVCH:
  441. while (mid>0 && m_bmk[mid].flags&BMK)
  442.   --mid;
  443. break;
  444.       case SNEXTICH:
  445. while (mid<m_bmk.GetSize() && m_bmk[mid].ref==p && !(m_bmk[mid].flags&BMK))
  446.   ++mid;
  447. break;
  448.       case SNEXTCH:
  449. while (mid<m_bmk.GetSize() && m_bmk[mid].flags&BMK)
  450.   ++mid;
  451. break;
  452.       case SPREVANY: case SNEXTANY:
  453. break;
  454.       case SPREVBMK:
  455. while (mid>0 && !(m_bmk[mid].flags&BMK))
  456.   --mid;
  457. if (!(m_bmk[mid].flags&BMK))
  458.   return -1;
  459. break;
  460.       }
  461.       return mid;
  462.     }
  463.   }
  464.   // no exact match, this is expected
  465.   switch (type) {
  466.   case SPREVBMK:
  467.     while (high>0 && !(m_bmk[high].flags&BMK))
  468.       --high;
  469.     if (high<0 || !(m_bmk[high].flags&BMK))
  470.       return -1;
  471.     break;
  472.   case SPREVCH:
  473.     while (high>0 && m_bmk[high].flags&BMK)
  474.       --high;
  475.     break;
  476.   case SNEXTICH:
  477.     return low;
  478.   case SNEXTCH:
  479.     while (low<m_bmk.GetSize() && m_bmk[low].flags&BMK)
  480.       ++low;
  481.   case SNEXTANY:
  482.     return low;
  483.   case SPREVANY:
  484.     break;
  485.   }
  486.   return high<0 ? 0 : high;
  487. }
  488. int   Bookmarks::UserBookmarks() {
  489.   int   n=0;
  490.   for (int i=0;i<m_bmk.GetSize();++i)
  491.     if (m_bmk[i].flags&BMK)
  492.       ++n;
  493.   return n;
  494. }
  495. void  Bookmarks::NormalizeLevels() {
  496.   int minlevel=-1;
  497.   for (int ii=0;ii<m_bmk.GetSize();++ii) {
  498.     if (m_bmk[ii].flags&BMK)
  499.       continue;
  500.     if (minlevel<0 || minlevel>m_bmk[ii].level)
  501.       minlevel=m_bmk[ii].level;
  502.   }
  503.   m_topbmk=m_ubmk=0;
  504.   for (int jj=0;jj<m_bmk.GetSize();++jj) {
  505.     if (m_bmk[jj].flags&BMK) {
  506.       ++m_topbmk;
  507.       ++m_ubmk;
  508.       continue;
  509.     }
  510.     if ((m_bmk[jj].level-=minlevel)==0)
  511.       ++m_topbmk;
  512.   }
  513. }
  514. bool  Bookmarks::BookmarksInRange(FilePos start,FilePos end) {
  515.   if (!m_ubmk) // shortcut when there are no user bookmarks
  516.     return false;
  517.   // ok, have to do some searching
  518.   int ptr=BFind(start,SNEXTANY);
  519.   if (ptr>=m_bmk.GetSize())
  520.     return false;
  521.   while (m_bmk[ptr].ref<end) {
  522.     if (m_bmk[ptr].flags&BMK)
  523.       return true;
  524.     ++ptr;
  525.   }
  526.   return false;
  527. }
  528. bool  Bookmarks::BookmarkFind(FilePos& start,FilePos end) {
  529.   if (!m_ubmk) // shortcut when there are no user bookmarks
  530.     return false;
  531.   // ok, have to do some searching
  532.   int ptr=BFind(start,SNEXTANY);
  533.   while (ptr<m_bmk.GetSize() && m_bmk[ptr].ref<end) {
  534.     if (m_bmk[ptr].flags&BMK) {
  535.       start=m_bmk[ptr].ref;
  536.       return true;
  537.     }
  538.     ++ptr;
  539.   }
  540.   return false;
  541. }
  542. static void  utf8write(HANDLE hFile,const CString& str) {
  543.   Buffer<char> utf8(Unicode::ToUtf8(str));
  544.   DWORD nw;
  545.   // XXX no error checking
  546.   WriteFile(hFile,utf8,utf8.size(),&nw,NULL);
  547. }
  548. bool  Bookmarks::ExportAllBookmarks(const CString& destfile) {
  549.   // open dest file
  550.   HANDLE  hFile=::CreateFile(destfile,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
  551.   if (hFile==NULL)
  552.     return false;
  553.   // open registry entry
  554.   HKEY   hBmk=AfxGetApp()->GetSectionKey(_T("Bookmarks"));
  555.   if (hBmk==NULL) {
  556.     ::CloseHandle(hFile);
  557.     return false;
  558.   }
  559.   // write utf-8 BOM
  560.   DWORD nw;
  561.   ::WriteFile(hFile,"xefxbbxbf",3,&nw,NULL);
  562.   // enumerate files
  563.   for (int i=0;;++i) {
  564.     CString   name;
  565.     DWORD     namelen=1024;
  566.     TCHAR     *namep=name.GetBuffer(namelen);
  567.     CString   cls;
  568.     DWORD     clslen=1024;
  569.     TCHAR     *clsp=cls.GetBuffer(clslen);
  570.     FILETIME  wtime;
  571.     DWORD     type;
  572.     if (::RegEnumKeyEx(hBmk,i,namep,&namelen,NULL,clsp,&clslen,&wtime)!=ERROR_SUCCESS)
  573.       break;
  574.     name.ReleaseBuffer(namelen);
  575.     cls.ReleaseBuffer(clslen);
  576.     // we have a file name at this point
  577.     name=unescape(name);
  578.     // enumerate bookmarks
  579.     HKEY  hKey;
  580.     if (::RegOpenKeyEx(hBmk,name,0,HR_REG_PERM,&hKey)==ERROR_SUCCESS) {
  581.       // get default settings
  582.       clslen=4096;
  583.       clsp=cls.GetBuffer(clslen);
  584.       clslen<<=1;
  585.       if (::RegQueryValueEx(hKey,NULL,NULL,&type,(LPBYTE)clsp,&clslen)==ERROR_SUCCESS && type==REG_SZ)
  586.       {
  587. cls.ReleaseBuffer();
  588. // write file header
  589. CString tmp;
  590. tmp.Format(_T("-%s,%srn"),(const TCHAR *)cls,(const TCHAR *)escape3(name));
  591. utf8write(hFile,tmp);
  592. for (int j=0;;++j) {
  593.   name.Format(_T("%d"),j);
  594.   clslen=4096;
  595.   clsp=cls.GetBuffer(clslen);
  596.   clslen<<=1;
  597.   if (::RegQueryValueEx(hKey,name,NULL,&type,(LPBYTE)clsp,&clslen)!=ERROR_SUCCESS || type!=REG_SZ)
  598.     break;
  599.   cls.ReleaseBuffer();
  600.   tmp.Format(_T(" %srn"),(const TCHAR *)escape3(cls));
  601.   utf8write(hFile,tmp);
  602. }
  603.       }
  604.       ::RegCloseKey(hKey);
  605.     }
  606.   }
  607.   // close file
  608.   ::CloseHandle(hFile);
  609.   ::RegCloseKey(hBmk);
  610.   return true;
  611. }