 qtools/qsortedlist.h       |   1 -
 src/codepointmap.h         | 133 ++++++++++++++++++++++
 src/index.cpp              | 268 ++++++++++++++++++++++++---------------------
 src/search.js              |  10 +-
 src/search_functions.php   |   2 +-
 src/search_functions_php.h |   2 +-
 src/search_js.h            |  10 +-
 src/searchindex.cpp        |  96 ++++++++--------
 src/util.cpp               |  73 ++++++++++++
 src/util.h                 |   8 ++
 10 files changed, 424 insertions(+), 179 deletions(-)

diff --git a/qtools/qsortedlist.h b/qtools/qsortedlist.h
index aeadd90..4616d1c 100644
--- a/qtools/qsortedlist.h
+++ b/qtools/qsortedlist.h
@@ -48,7 +48,6 @@ template<class type> class Q_EXPORT QSortedList : public QList<type>
 public:
     QSortedList() {}
     QSortedList( const QSortedList<type> &l ) : QList<type>(l) {}
-    ~QSortedList() { clear(); }
     QSortedList<type> &operator=(const QSortedList<type> &l)
       { return (QSortedList<type>&)QList<type>::operator=(l); }
 
diff --git a/src/codepointmap.h b/src/codepointmap.h
new file mode 100644
index 0000000..2cd2c8f
--- /dev/null
+++ b/src/codepointmap.h
@@ -0,0 +1,133 @@
+/******************************************************************************
+ *
+ * 
+ *
+ * Copyright (C) 2013 by Suzumizaki-Kimitaka.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby 
+ * granted. No representations are made about the suitability of this software 
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#ifndef CODEPOINTMAP_H
+#define CODEPOINTMAP_H
+
+#include <qsortedlist.h>
+
+/*! \file
+    \brief helper classes for indexing entries by unicode
+
+The classes defined in this file are used in searchindex.cpp and index.cpp.
+
+In fact, these classes are not required if we can use std::map,
+because std::map is defined as sorted map.
+*/
+
+/*! \brief The relationship between unicode codepoint and pointer to any type
+
+The code point means the numbers assigned to each characters as unicode.
+
+This class is designed to use with CodePointToSortedList&lt; T &gt;
+Compairing functions are used only sorting with each QSortedList inherited
+by CodePointToSortedList.
+
+This class doesn't manage given resource pointed as T* (stored to m_listPtr).
+CodePointToSortedList&lt; T &gt; will delete it.
+*/
+template <class T> class CodePointPair
+{
+public:
+    CodePointPair() :
+        m_codePoint( 0 ), m_listPtr( 0 )
+        { }
+    CodePointPair( const CodePointPair& src ) :
+        m_codePoint( src.m_codePoint ), m_listPtr( src.m_listPtr )
+        { }
+    CodePointPair( uint codePoint, T* ptrToList ) :
+        m_codePoint( codePoint ), m_listPtr( ptrToList )
+        { }
+    uint getFirst() const { return m_codePoint; }
+    T* getSecond() const { return m_listPtr; }
+    const bool operator==( const CodePointPair& rhs ) const
+    {
+        return m_codePoint == rhs.m_codePoint;
+    }
+    const bool operator<( const CodePointPair& rhs ) const
+    {
+        return m_codePoint < rhs.m_codePoint;
+    }
+private:
+    uint m_codePoint;
+    T* m_listPtr;
+};
+
+
+/*! \brief Represents map from code point to some resource
+
+\sa CodePointPair&lt; T &gt;
+*/
+template <class T> class CodePointToSortedList : protected QSortedList< CodePointPair< T > >
+{
+public:
+    typedef CodePointPair< T > PairClass;
+    typedef QSortedList< PairClass > BaseClass;
+    CodePointToSortedList()
+    {
+        BaseClass::setAutoDelete( true );
+    }
+    virtual ~CodePointToSortedList()
+    {
+        for ( PairClass* ptr = first() ;
+              ptr != 0 ;
+              ptr = next() )
+        {
+            delete ( ptr->getSecond() );
+        }
+    }
+    int compareItems( QCollection::Item item1, QCollection::Item item2 )
+    {
+        PairClass* p1 = (PairClass*) item1;
+        PairClass* p2 = (PairClass*) item2;
+        return int( p1->getFirst() ) - int( p2->getFirst() );
+    }
+    T& operator [] ( const uint key )
+    {
+        PairClass* pairPtr = find( key );
+        if ( pairPtr )
+        {
+            return *( pairPtr->getSecond() );
+        }
+        T* ptr = new T;
+        pairPtr = new PairClass( key, ptr );
+        inSort( pairPtr );
+        return *ptr;
+    }
+    PairClass* first()
+    {
+        return BaseClass::first();
+    }
+    PairClass* next()
+    {
+        return BaseClass::next();
+    }
+    PairClass* find( const uint key )
+    {
+        PairClass dmy( key, 0 );
+        const int idx = BaseClass::find( &dmy );
+        if ( idx != -1 ) { return BaseClass::at( idx ); }
+        return 0;
+    }
+    void clear()
+    {
+        BaseClass::clear();
+    }
+};
+
+#endif
+
diff --git a/src/index.cpp b/src/index.cpp
index 5970a43..68897f0 100644
--- a/src/index.cpp
+++ b/src/index.cpp
@@ -25,6 +25,7 @@
 #include <qdatetime.h>
 #include <qdir.h>
 #include <qregexp.h>
+#include <qlist.h>
 
 #include "message.h"
 #include "index.h"
@@ -47,11 +48,11 @@
 #include "classlist.h"
 #include "namespacedef.h"
 #include "filename.h"
+#include "codepointmap.h"
 
 #define MAX_ITEMS_BEFORE_MULTIPAGE_INDEX 200
 #define MAX_ITEMS_BEFORE_QUICK_INDEX 30
 
-
 int annotatedClasses;
 int annotatedClassesPrinted;
 int hierarchyClasses;
@@ -129,13 +130,12 @@ class MemberIndexList : public QList<MemberDef>
     }
 };
 
-#define MEMBER_INDEX_ENTRIES 256
-
-static MemberIndexList g_memberIndexLetterUsed[CMHL_Total][MEMBER_INDEX_ENTRIES];
-static MemberIndexList g_fileIndexLetterUsed[FMHL_Total][MEMBER_INDEX_ENTRIES];
-static MemberIndexList g_namespaceIndexLetterUsed[NMHL_Total][MEMBER_INDEX_ENTRIES];
+typedef CodePointToSortedList< MemberIndexList > CodePointToMemberIndexList;
+typedef CodePointToMemberIndexList::PairClass CodePointAndMemberIndexListPair;
 
-//static bool g_classIndexLetterUsed[CHL_Total][256];
+static CodePointToMemberIndexList g_memberIndexLetterUsed[CMHL_Total];
+static CodePointToMemberIndexList g_fileIndexLetterUsed[FMHL_Total];
+static CodePointToMemberIndexList g_namespaceIndexLetterUsed[NMHL_Total];
 
 const int maxItemsBeforeQuickIndex = MAX_ITEMS_BEFORE_QUICK_INDEX;
 
@@ -1690,12 +1690,34 @@ static void writeAnnotatedClassList(OutputList &ol)
   ol.endIndexList();
 }
 
-static QCString letterToLabel(char startLetter)
+
+/*! Generate the string used to postfix the anchor label
+
+If given value makes isId() returns false, this function returns "0xHH".
+Otherwise, this function returns utf-8 string.
+
+Currently isId() returns true when the value over 0x7F is given.
+
+for HTML5, there is no limit to use any character with "id" attribute.
+"name" attribute cannot contain non ASCII chararters, but major browsers
+can support and "name" is deprecated (should be replaced with "id").
+
+@param startLetter The integer represents a unicode character, 0 - MAX_UNICODE_CODEPOINT
+@return The utf-8 string to append to anchor label "letter_"
+*/
+static QCString letterToLabel(uint startLetter)
 {
   QCString s(5); 
   if (isId(startLetter))
   {
-    s[0]=startLetter; s[1]=0;
+    if ( startLetter < 0x7f )
+    {
+      s[0]=startLetter; s[1]=0;
+    }
+    else
+    {
+      s = QString( QChar( startLetter ) ).utf8();
+    }
   }
   else
   {
@@ -1709,6 +1731,7 @@ static QCString letterToLabel(char startLetter)
   return s;
 }
 
+
 //----------------------------------------------------------------------------
 
 /** Special class list where sorting takes IGNORE_PREFIX into account. */
@@ -1726,23 +1749,28 @@ public:
   }
 };
 
+
+typedef CodePointToSortedList< PrefixIgnoreClassList > CodePointToPrefixIngoreClassList;
+typedef CodePointToPrefixIngoreClassList::PairClass CodePointAndPrefixIngoreClassPair;
+
+
 /** Class representing a cell in the alphabetical class index. */
 class AlphaIndexTableCell
 {
   public:
-    AlphaIndexTableCell(int row,int col,uchar letter,ClassDef *cd) : 
+    AlphaIndexTableCell(int row,int col,uint letter,ClassDef *cd) : 
       m_letter(letter), m_class(cd), m_row(row), m_col(col) 
     { //printf("AlphaIndexTableCell(%d,%d,%c,%s)\n",row,col,letter!=0 ? letter: '-',
       //       cd!=(ClassDef*)0x8 ? cd->name().data() : "<null>"); 
     }
 
     ClassDef *classDef() const { return m_class; }
-    uchar letter()       const { return m_letter; }
+    uint  letter()       const { return m_letter; }
     int row()            const { return m_row; }
     int column()         const { return m_col; }
 
   private:
-    uchar m_letter;
+    uint m_letter;
     ClassDef *m_class;
     int m_row;
     int m_col;
@@ -1770,13 +1798,38 @@ class AlphaIndexTableColumns : public QList<AlphaIndexTableRows>
     AlphaIndexTableColumns() { setAutoDelete(TRUE); }
 };
 
+class UsedIndexLetters : public QList<uint>
+{
+  public:
+    UsedIndexLetters() : QList<uint>() {}
+    ~UsedIndexLetters() {}
+    int compareItems(QCollection::Item item1, QCollection::Item item2)
+    {
+      int *p1=(int *)item1;
+      int *p2=(int *)item2;
+      return *p1 - *p2; // subtracting is done by int not uint.
+    }
+    void uniqueInSort( const uint key )
+    {
+      uint* v = new uint( key );
+      if ( find( v ) == -1 )
+      {
+          inSort( v );
+      }
+      else
+      {
+          delete v;
+      }
+    }
+};
+
 // write an alphabetical index of all class with a header for each letter
 static void writeAlphabeticalClassList(OutputList &ol)
 {
   // What starting letters are used
-  bool indexLetterUsed[256];
-  memset (indexLetterUsed, 0, sizeof (indexLetterUsed));
-
+  UsedIndexLetters indexLetterUsed;
+  indexLetterUsed.setAutoDelete( true );
+  
   // first count the number of headers
   ClassSDict::Iterator cli(*Doxygen::classSDict);
   ClassDef *cd;
@@ -1788,27 +1841,23 @@ static void writeAlphabeticalClassList(OutputList &ol)
     {
       if (cd->getLanguage()==SrcLangExt_VHDL && !((VhdlDocGen::VhdlClasses)cd->protection()==VhdlDocGen::ENTITYCLASS ))// no architecture
         continue;
-	     
+
       int index = getPrefixIndex(cd->className());
       //printf("name=%s index=%d %d\n",cd->className().data(),index,cd->protection());
-      startLetter=toupper(cd->className().at(index))&0xFF;
-      indexLetterUsed[startLetter] = true;
+      startLetter=getNextUtf8OrToUpper( cd->className(), index );
+      indexLetterUsed.uniqueInSort( startLetter );
     }
   }
 
   // write quick link index (row of letters)
   QCString alphaLinks = "<div class=\"qindex\">";
-  int l;
-  for (l=0; l<256; l++)
+  for ( uint* ptr = indexLetterUsed.first() ; ptr ; ptr = indexLetterUsed.next() )
   {
-    if (indexLetterUsed[l])
-    {
       if (headerItems) alphaLinks += "&#160;|&#160;";
       headerItems++;
+      QCString lc = QString( QChar( *ptr ) ).utf8();
       alphaLinks += (QCString)"<a class=\"qindex\" href=\"#letter_" + 
-                    (char)l + "\">" + 
-                    (char)l + "</a>";
-    }
+                    lc + "\">" + lc + "</a>";
   }
   alphaLinks += "</div>\n";
   ol.writeString(alphaLinks);
@@ -1825,7 +1874,7 @@ static void writeAlphabeticalClassList(OutputList &ol)
   //    headerItems,totalItems,columns,rows,itemsInLastRow);
 
   // Keep a list of classes for each starting letter
-  PrefixIgnoreClassList classesByLetter[256];
+  CodePointToPrefixIngoreClassList classesByLetter;
   AlphaIndexTableColumns tableColumns;
 
   // fill the columns with the class list (row elements in each column,
@@ -1841,7 +1890,7 @@ static void writeAlphabeticalClassList(OutputList &ol)
     if (cd->isLinkableInProject() && cd->templateMaster()==0)
     {
       int index = getPrefixIndex(cd->className());
-      startLetter=toupper(cd->className().at(index))&0xFF;
+      startLetter=getNextUtf8OrToUpper(cd->className(),index);
       // Do some sorting again, since the classes are sorted by name with 
       // prefix, which should be ignored really.
       if (cd->getLanguage()==SrcLangExt_VHDL)
@@ -1871,26 +1920,27 @@ static void writeAlphabeticalClassList(OutputList &ol)
   AlphaIndexTableRows *tableRows = new AlphaIndexTableRows;
   tableColumns.append(tableRows);
   int col=0,row=0,maxRows=0;
-  for (l=0; l<256; l++)
+  for ( CodePointAndPrefixIngoreClassPair* ptr = classesByLetter.first() ;
+        ptr ; 
+        ptr = classesByLetter.next() )
   {
-    if (classesByLetter[l].count()>0)
-    {
+      const uint l = ptr->getFirst();
+      PrefixIgnoreClassList* ptrSecond = ptr->getSecond();
       // add special header cell
-      tableRows->append(new AlphaIndexTableCell(row,col,(uchar)l,(ClassDef*)0x8));
+      tableRows->append(new AlphaIndexTableCell(row,col,l,(ClassDef*)0x8));
       row++;
       tableRows->append(new AlphaIndexTableCell(row,col,0,(ClassDef*)0x8));
       row++;
-      tableRows->append(new AlphaIndexTableCell(row,col,0,classesByLetter[l].at(0)));
+      tableRows->append(new AlphaIndexTableCell(row,col,0,ptrSecond->at(0)));
       row++; 
       NEXT_ROW();
-      for (i=1; i<(int)classesByLetter[l].count(); i++)
+      for (i=1; i<(int)ptrSecond->count(); i++)
       {
         // add normal cell
-        tableRows->append(new AlphaIndexTableCell(row,col,0,classesByLetter[l].at(i)));
+        tableRows->append(new AlphaIndexTableCell(row,col,0,ptrSecond->at(i)));
         row++;
         NEXT_ROW();
       }
-    }
   }
 
   // create row iterators for each column
@@ -2163,15 +2213,21 @@ static void writeNamespaceLinkForMember(OutputList &ol,MemberDef *md,const char
   }
 }
 
+
 static void writeMemberList(OutputList &ol,bool useSections,int page,
-                            MemberIndexList memberLists[MEMBER_INDEX_ENTRIES],
+                            CodePointToMemberIndexList& memberLists,
                             DefinitionIntf::DefType type)
 {
-  int pi;
-  // page==-1 => write all member indices to one page (used when total members is small)
-  // page!=-1 => write all member for this page only (used when total member is large)
-  int startIndex = page==-1 ? 0                      : page;
-  int endIndex   = page==-1 ? MEMBER_INDEX_ENTRIES-1 : page;
+  uint pi;
+  CodePointAndMemberIndexListPair* ptr;
+  if ( page != -1 )
+  {
+    ptr = memberLists.find( page );
+  }
+  else
+  {
+    ptr = memberLists.first();
+  }
   ASSERT((int)type<3);
 
   typedef void (*writeLinkForMember_t)(OutputList &ol,MemberDef *md,const char *separator,
@@ -2189,10 +2245,9 @@ static void writeMemberList(OutputList &ol,bool useSections,int page,
   bool first=TRUE;
   bool firstSection=TRUE;
   bool firstItem=TRUE;
-  for (pi=startIndex; pi<=endIndex; pi++) // page==-1 => pi=[0..127], page!=-1 => pi=page 
+  for ( ; ptr ; ptr = memberLists.next() ) // if page != -1, break loop at the end.
   {
-    MemberIndexList *ml = &memberLists[pi];
-    if (ml->count()==0) continue;
+    MemberIndexList *ml = ptr->getSecond();
     ml->sort();
     QListIterator<MemberDef> mli(*ml);
     MemberDef *md;
@@ -2205,18 +2260,16 @@ static void writeMemberList(OutputList &ol,bool useSections,int page,
       int startIndex = getPrefixIndex(name);
       if (QCString(name.data()+startIndex)!=prevName) // new entry
       {
+        const uint newHeadChar = getNextUtf8OrToLower( name, startIndex );
         if ((prevName.isEmpty() || 
-            tolower(name.at(startIndex))!=tolower(prevName.at(0))) && 
+            newHeadChar != getNextUtf8OrToLower(prevName , 0) ) && 
             useSections) // new section
         {
           if (!firstItem)    ol.endItemListItem();
           if (!firstSection) ol.endItemList();
-          char cl[2];
-          cl[0] = tolower(name.at(startIndex));
-          cl[1] = 0;
-          QCString cs = letterToLabel(cl[0]);
+          QCString cs = letterToLabel( newHeadChar );
           QCString anchor=(QCString)"index_"+cs;
-          QCString title=(QCString)"- "+cl+" -";
+          QCString title= (QString("- ")+QChar(newHeadChar)+" -").utf8();
           ol.startSection(anchor,title,SectionInfo::Subsection);
           ol.docify(title);
           ol.endSection(anchor,SectionInfo::Subsection);
@@ -2251,6 +2304,7 @@ static void writeMemberList(OutputList &ol,bool useSections,int page,
       // write the link for the specific list type
       writeLinkForMemberMap[(int)type](ol,md,sep,prevDefName);
     }
+    if ( page != -1 ) { break; }
   }
   if (!firstItem) ol.endItemListItem();
   ol.endItemList();
@@ -2265,20 +2319,15 @@ void initClassMemberIndices()
   for (j=0;j<CMHL_Total;j++)
   {
     documentedClassMembers[j]=0;
-    for (i=0;i<MEMBER_INDEX_ENTRIES;i++) 
-    {
-      g_memberIndexLetterUsed[j][i].clear();
-    }
+    g_memberIndexLetterUsed[j].clear();
   }
 }
 
+
 void addClassMemberNameToIndex(MemberDef *md)
 {
   static bool hideFriendCompounds = Config_getBool("HIDE_FRIEND_COMPOUNDS");
   ClassDef *cd=0;
-
- 
-  
   if (md->isLinkableInProject() && 
       (cd=md->getClassDef())    && 
       cd->isLinkableInProject() &&
@@ -2286,8 +2335,7 @@ void addClassMemberNameToIndex(MemberDef *md)
   {
     QCString n = md->name();
     int index = getPrefixIndex(n);
-    uchar charCode = (uchar)n.at(index);
-    uint letter = charCode<128 ? tolower(charCode) : charCode;
+    uint letter = getNextUtf8OrToLower( n, index );
     if (!n.isEmpty()) 
     {
       bool isFriendToHide = hideFriendCompounds &&
@@ -2299,7 +2347,7 @@ void addClassMemberNameToIndex(MemberDef *md)
         g_memberIndexLetterUsed[CMHL_All][letter].append(md);
         documentedClassMembers[CMHL_All]++;
       }
-      if (md->isFunction()  || md->isSlot() || md->isSignal())
+      if (md->isFunction() || md->isSlot() || md->isSignal())
       {
         g_memberIndexLetterUsed[CMHL_Functions][letter].append(md);
         documentedClassMembers[CMHL_Functions]++;
@@ -2348,15 +2396,11 @@ void addClassMemberNameToIndex(MemberDef *md)
 
 void initNamespaceMemberIndices()
 {
-  int i=0;
   int j=0;
   for (j=0;j<NMHL_Total;j++)
   {
     documentedNamespaceMembers[j]=0;
-    for (i=0;i<MEMBER_INDEX_ENTRIES;i++) 
-    {
-      g_namespaceIndexLetterUsed[j][i].clear();
-    }
+    g_namespaceIndexLetterUsed[j].clear();
   }
 }
 
@@ -2367,8 +2411,7 @@ void addNamespaceMemberNameToIndex(MemberDef *md)
   {
     QCString n = md->name();
     int index = getPrefixIndex(n);
-    uchar charCode = (uchar)n.at(index);
-    uint letter = charCode<128 ? tolower(charCode) : charCode;
+    uint letter = getNextUtf8OrToLower( n, index );
     if (!n.isEmpty()) 
     {
       g_namespaceIndexLetterUsed[NMHL_All][letter].append(md);
@@ -2407,15 +2450,11 @@ void addNamespaceMemberNameToIndex(MemberDef *md)
 
 void initFileMemberIndices()
 {
-  int i=0;
   int j=0;
   for (j=0;j<NMHL_Total;j++)
   {
     documentedFileMembers[j]=0;
-    for (i=0;i<MEMBER_INDEX_ENTRIES;i++) 
-    {
-      g_fileIndexLetterUsed[j][i].clear();
-    }
+    g_fileIndexLetterUsed[j].clear();
   }
 }
 
@@ -2426,8 +2465,7 @@ void addFileMemberNameToIndex(MemberDef *md)
   {
     QCString n = md->name();
     int index = getPrefixIndex(n);
-    uchar charCode = (uchar)n.at(index);
-    uint letter = charCode<128 ? tolower(charCode) : charCode;
+    uint letter = getNextUtf8OrToLower( n, index );
     if (!n.isEmpty()) 
     {
       g_fileIndexLetterUsed[FMHL_All][letter].append(md);
@@ -2470,18 +2508,18 @@ void addFileMemberNameToIndex(MemberDef *md)
 //----------------------------------------------------------------------------
 
 static void writeQuickMemberIndex(OutputList &ol,
-    MemberIndexList charUsed[MEMBER_INDEX_ENTRIES],int page,
+    CodePointToMemberIndexList& charUsed,uint page,
     QCString fullName,bool multiPage)
 {
   bool first=TRUE;
-  int i;
   startQuickIndexList(ol,TRUE);
-  for (i=33;i<127;i++)
+  for ( CodePointAndMemberIndexListPair* ptr = charUsed.first();
+        ptr ;
+        ptr = charUsed.next() )
   {
-    char is[2];is[0]=(char)i;is[1]='\0';
-    QCString ci = letterToLabel((char)i);
-    if (charUsed[i].count()>0)
-    {
+      const uint i = ptr->getFirst();
+      QCString ci = letterToLabel(i);
+      QCString is = QString( QChar( i ) ).utf8();
       QCString anchor;
       QCString extension=Doxygen::htmlFileExtension;
       if (!multiPage)
@@ -2494,7 +2532,6 @@ static void writeQuickMemberIndex(OutputList &ol,
       ol.writeString(is);
       endQuickIndexItem(ol);
       first=FALSE;
-    }
   }
   endQuickIndexList(ol);
 }
@@ -2538,11 +2575,9 @@ static void writeClassMemberIndexFiltered(OutputList &ol, ClassMemberHighlight h
   static bool disableIndex     = Config_getBool("DISABLE_INDEX");
 
   bool multiPageIndex=FALSE;
-  int numPages=1;
   if (documentedClassMembers[hl]>MAX_ITEMS_BEFORE_MULTIPAGE_INDEX)
   {
     multiPageIndex=TRUE;
-    numPages=127;
   }
 
   ol.pushGeneratorState();
@@ -2561,25 +2596,23 @@ static void writeClassMemberIndexFiltered(OutputList &ol, ClassMemberHighlight h
     if (multiPageIndex) Doxygen::indexList->incContentsDepth();
   }
 
-  int page;
   bool first=TRUE;
-  for (page=0;page<numPages;page++)
+  uint page;
+  CodePointAndMemberIndexListPair* it = g_memberIndexLetterUsed[hl].first();
+  // At least one page exists here, checked at the top of this function.
+  for ( page = 0; first || it; it = g_memberIndexLetterUsed[hl].next() )
   {
-    if (!multiPageIndex || g_memberIndexLetterUsed[hl][page].count()>0)
-    {
       QCString fileName = getCmhlInfo(hl)->fname;
       if (multiPageIndex)
       { 
+        page = it->getFirst();
         if (!first)
         {
           fileName+=QCString().sprintf("_0x%02x",page);
         }
-        char cs[2];
-        cs[0]=page;
-        cs[1]=0;
         if (addToIndex)
         {
-          Doxygen::indexList->addContentsItem(FALSE,cs,0,fileName,0,FALSE,TRUE);
+          Doxygen::indexList->addContentsItem(FALSE,QString( QChar( page ) ).utf8(),0,fileName,0,FALSE,TRUE);
         }
       }
       bool quickIndex = documentedClassMembers[hl]>maxItemsBeforeQuickIndex;
@@ -2645,7 +2678,6 @@ static void writeClassMemberIndexFiltered(OutputList &ol, ClassMemberHighlight h
                       Definition::TypeClass);
       endFile(ol);
       first=FALSE;
-    }
   }
 
   if (multiPageIndex && addToIndex) Doxygen::indexList->decContentsDepth();
@@ -2716,11 +2748,9 @@ static void writeFileMemberIndexFiltered(OutputList &ol, FileMemberHighlight hl)
   static bool disableIndex     = Config_getBool("DISABLE_INDEX");
 
   bool multiPageIndex=FALSE;
-  int numPages=1;
   if (documentedFileMembers[hl]>MAX_ITEMS_BEFORE_MULTIPAGE_INDEX)
   {
     multiPageIndex=TRUE;
-    numPages=127;
   }
 
   ol.pushGeneratorState();
@@ -2738,25 +2768,23 @@ static void writeFileMemberIndexFiltered(OutputList &ol, FileMemberHighlight hl)
     if (multiPageIndex) Doxygen::indexList->incContentsDepth();
   }
 
-  int page;
   bool first=TRUE;
-  for (page=0;page<numPages;page++)
+  uint page;
+  CodePointAndMemberIndexListPair* it = g_fileIndexLetterUsed[hl].first();
+  // At least one page exists here, checked at the top of this function.
+  for ( page = 0; first || it ; it = g_fileIndexLetterUsed[hl].next() )
   {
-    if (!multiPageIndex || g_fileIndexLetterUsed[hl][page].count()>0)
-    {
       QCString fileName = getFmhlInfo(hl)->fname;
       if (multiPageIndex)
       {
+        page = it->getFirst();
         if (!first)
         {
           fileName+=QCString().sprintf("_0x%02x",page);
         }
-        char cs[2];
-        cs[0]=page;
-        cs[1]=0;
         if (addToIndex)
         {
-          Doxygen::indexList->addContentsItem(FALSE,cs,0,fileName,0,FALSE,TRUE);
+          Doxygen::indexList->addContentsItem(FALSE,QString( QChar( page ) ).utf8(),0,fileName,0,FALSE,TRUE);
         }
       }
       bool quickIndex = documentedFileMembers[hl]>maxItemsBeforeQuickIndex;
@@ -2821,7 +2849,6 @@ static void writeFileMemberIndexFiltered(OutputList &ol, FileMemberHighlight hl)
           Definition::TypeFile);
       endFile(ol);
       first=FALSE;
-    }
   }
   if (multiPageIndex && addToIndex) Doxygen::indexList->decContentsDepth();
   ol.popGeneratorState();
@@ -2890,11 +2917,9 @@ static void writeNamespaceMemberIndexFiltered(OutputList &ol,
 
 
   bool multiPageIndex=FALSE;
-  int numPages=1;
   if (documentedNamespaceMembers[hl]>MAX_ITEMS_BEFORE_MULTIPAGE_INDEX)
   {
     multiPageIndex=TRUE;
-    numPages=127;
   }
 
   ol.pushGeneratorState();
@@ -2912,25 +2937,23 @@ static void writeNamespaceMemberIndexFiltered(OutputList &ol,
     if (multiPageIndex) Doxygen::indexList->incContentsDepth();
   }
 
-  int page;
   bool first=TRUE;
-  for (page=0;page<numPages;page++)
+  uint page;
+  CodePointAndMemberIndexListPair* it = g_namespaceIndexLetterUsed[hl].first();
+  // At least one page exists here, checked at the top of this function.
+  for ( page = 0; first || it ; it = g_namespaceIndexLetterUsed[hl].next() )
   {
-    if (!multiPageIndex || g_namespaceIndexLetterUsed[hl][page].count()>0)
-    {
       QCString fileName = getNmhlInfo(hl)->fname;
       if (multiPageIndex)
       {
+        page = it->getFirst();
         if (!first)
         {
           fileName+=QCString().sprintf("_0x%02x",page);
         }
-        char cs[2];
-        cs[0]=page;
-        cs[1]=0;
         if (addToIndex)
         {
-          Doxygen::indexList->addContentsItem(FALSE,cs,0,fileName,0,FALSE,TRUE);
+          Doxygen::indexList->addContentsItem(FALSE,QString( QChar( page ) ).utf8(),0,fileName,0,FALSE,TRUE);
         }
       }
       bool quickIndex = documentedNamespaceMembers[hl]>maxItemsBeforeQuickIndex;
@@ -2996,7 +3019,6 @@ static void writeNamespaceMemberIndexFiltered(OutputList &ol,
                       g_namespaceIndexLetterUsed[hl],
                       Definition::TypeNamespace);
       endFile(ol);
-    }
   }
   if (multiPageIndex && addToIndex) Doxygen::indexList->decContentsDepth();
   ol.popGeneratorState();
@@ -3100,7 +3122,7 @@ bool writeMemberNavIndex(FTextStream &t,
                          int indent,
                          int n,
                          int documentedMembers[],
-                         MemberIndexList indexLetterUsed[][MEMBER_INDEX_ENTRIES],
+                         CodePointToMemberIndexList indexLetterUsed[],
                          const T *(*getInfo)(int),
                          bool &first
                         )
@@ -3136,11 +3158,11 @@ bool writeMemberNavIndex(FTextStream &t,
       if (quickIndex)
       {
         t << "[ " << endl;
-        int j;
-        for (j=33;j<127;j++)
+        for ( CodePointAndMemberIndexListPair* ptr = indexLetterUsed[i].first() ;
+              ptr ;
+              ptr = indexLetterUsed[i].next() )
         {
-          if (indexLetterUsed[i][j].count()>0)
-          {
+            const uint j = ptr->getFirst();
             if (!firstPage) t << "," << endl;
             QCString fullName = getInfo(i)->fname;
             QCString extension = Doxygen::htmlFileExtension;
@@ -3149,13 +3171,13 @@ bool writeMemberNavIndex(FTextStream &t,
               anchor=fullName+extension+"#index_";
             else 
               anchor=fullName+QCString().sprintf("_0x%02x",j)+extension+"#index_";
-            char is[2];is[0]=(char)j;is[1]='\0';
-            QCString ci = letterToLabel((char)j);
+            QString iss( QChar( j ) );
+            QCString is = iss.utf8();
+            QCString ci = letterToLabel(j);
             t << indentStr << "    [ ";
             t << "\"" << is << "\", ";
             t << "\"" << anchor << ci << "\", null ]";
             firstPage=FALSE;
-          }
         }
         t << endl << indentStr << "  ] ]";
       }
diff --git a/src/search.js b/src/search.js
index 3ed3f90..e279aeb 100644
--- a/src/search.js
+++ b/src/search.js
@@ -5,7 +5,7 @@ function convertToId(search)
   {
     var c = search.charAt(i);
     var cn = c.charCodeAt(0);
-    if (c.match(/[a-z0-9]/))
+    if (c.match(/[a-z0-9\u0080-\uFFFF]/))
     {
       result+=c;
     }
@@ -310,6 +310,12 @@ function SearchBox(name, resultsPath, inFrame, label)
     var searchValue = this.DOMSearchField().value.replace(/^ +/, "");
 
     var code = searchValue.toLowerCase().charCodeAt(0);
+    var idxChar = searchValue.substr(0, 1).toLowerCase();
+    if ( 0xD800 <= code && code <= 0xDBFF && searchValue > 1)
+    {
+      code = 0x10000 + ( code - 0xD800 ) * 0x400 + searchValue.charCodeAt(1) - 0xDC00;
+      idxChar = searchValue.substr(0, 2);
+    }
     var hexCode;
     if (code<16) 
     {
@@ -324,7 +330,7 @@ function SearchBox(name, resultsPath, inFrame, label)
     var resultsPageWithSearch;
     var hasResultsPage;
 
-    if (indexSectionsWithContent[this.searchIndex].charAt(code) == '1')
+    if (indexSectionsWithContent[this.searchIndex].indexOf(idxChar) != -1)
     {
        resultsPage = this.resultsPath + '/' + indexSectionNames[this.searchIndex] + '_' + hexCode + '.html';
        resultsPageWithSearch = resultsPage+'?'+escape(searchValue);
diff --git a/src/search_functions.php b/src/search_functions.php
index acd7f3c..5ad2e5d 100644
--- a/src/search_functions.php
+++ b/src/search_functions.php
@@ -358,7 +358,7 @@ function main()
   $sorted = run_query($query);
   // Now output the HTML stuff...
   // End the HTML form
-  end_form(preg_replace("/[^a-zA-Z0-9\-\_\.]/i", " ", $query ));
+  end_form(preg_replace("/[^a-zA-Z0-9\-\_\.\x80-\xFF]/i", " ", $query ));
   // report results to the user
   report_results($sorted);
   end_page();
diff --git a/src/search_functions_php.h b/src/search_functions_php.h
index b09c259..9c7c3e3 100644
--- a/src/search_functions_php.h
+++ b/src/search_functions_php.h
@@ -358,7 +358,7 @@
 "  $sorted = run_query($query);\n"
 "  // Now output the HTML stuff...\n"
 "  // End the HTML form\n"
-"  end_form(preg_replace(\"/[^a-zA-Z0-9\\-\\_\\.]/i\", \" \", $query ));\n"
+"  end_form(preg_replace(\"/[^a-zA-Z0-9\\-\\_\\.\\x80-\\xFF]/i\", \" \", $query ));\n"
 "  // report results to the user\n"
 "  report_results($sorted);\n"
 "  end_page();\n"
diff --git a/src/search_js.h b/src/search_js.h
index 985d03c..37940de 100644
--- a/src/search_js.h
+++ b/src/search_js.h
@@ -5,7 +5,7 @@
 "  {\n"
 "    var c = search.charAt(i);\n"
 "    var cn = c.charCodeAt(0);\n"
-"    if (c.match(/[a-z0-9]/))\n"
+"    if (c.match(/[a-z0-9\\u0080-\\uFFFF]/))\n"
 "    {\n"
 "      result+=c;\n"
 "    }\n"
@@ -310,6 +310,12 @@
 "    var searchValue = this.DOMSearchField().value.replace(/^ +/, \"\");\n"
 "\n"
 "    var code = searchValue.toLowerCase().charCodeAt(0);\n"
+"    var idxChar = searchValue.substr(0, 1).toLowerCase();\n"
+"    if ( 0xD800 <= code && code <= 0xDBFF && searchValue > 1)\n"
+"    {\n"
+"      code = 0x10000 + ( code - 0xD800 ) * 0x400 + searchValue.charCodeAt(1) - 0xDC00;\n"
+"      idxChar = searchValue.substr(0, 2);\n"
+"    }\n"
 "    var hexCode;\n"
 "    if (code<16) \n"
 "    {\n"
@@ -324,7 +330,7 @@
 "    var resultsPageWithSearch;\n"
 "    var hasResultsPage;\n"
 "\n"
-"    if (indexSectionsWithContent[this.searchIndex].charAt(code) == '1')\n"
+"    if (indexSectionsWithContent[this.searchIndex].indexOf(idxChar) != -1)\n"
 "    {\n"
 "       resultsPage = this.resultsPath + '/' + indexSectionNames[this.searchIndex] + '_' + hexCode + '.html';\n"
 "       resultsPageWithSearch = resultsPage+'?'+escape(searchValue);\n"
diff --git a/src/searchindex.cpp b/src/searchindex.cpp
index 0380b33..fc072a9 100644
--- a/src/searchindex.cpp
+++ b/src/searchindex.cpp
@@ -36,8 +36,9 @@
 #include "memberdef.h"
 #include "filename.h"
 #include "membername.h"
+#include "codepointmap.h"
 
-// file format: (all multi-byte values are stored in big endian format)
+// file format: (all multi-byte intergers are stored in big endian format)
 //   4 byte header
 //   256*256*4 byte index (4 bytes)
 //   for each index entry: a zero terminated list of words 
@@ -586,9 +587,6 @@ void SearchIndexExternal::write(const char *fileName)
 static const char search_script[]=
 #include "search_js.h"
 ;
-
-#define MEMBER_INDEX_ENTRIES   256
-
 #define SEARCH_INDEX_ALL         0
 #define SEARCH_INDEX_CLASSES     1
 #define SEARCH_INDEX_NAMESPACES  2
@@ -620,6 +618,7 @@ class SearchIndexList : public SDict< QList<Definition> >
       if (l==0)
       {
         l=new QList<Definition>;
+        // SDict doesn't sort automatically. we have to sort() later.
         SDict< QList<Definition> >::append(d->name(),l);
       }
       l->append(d);
@@ -634,8 +633,12 @@ class SearchIndexList : public SDict< QList<Definition> >
     }
 };
 
+
+typedef CodePointToSortedList< SearchIndexList > CodePointToSearchIndexList;
+typedef CodePointToSearchIndexList::PairClass CodePointAndSearchIndexListPair;
+
 static void addMemberToSearchIndex(
-         SearchIndexList symbols[NUM_SEARCH_INDICES][MEMBER_INDEX_ENTRIES],
+         CodePointToSearchIndexList* symbols,
          int symbolCount[NUM_SEARCH_INDICES],
          MemberDef *md)
 {
@@ -653,10 +656,9 @@ static void addMemberToSearchIndex(
      )
   {
     QCString n = md->name();
-    uchar charCode = (uchar)n.at(0);
-    uint letter = charCode<128 ? tolower(charCode) : charCode;
     if (!n.isEmpty()) 
     {
+      uint letter = getNextUtf8OrToLower(n, 0);
       bool isFriendToHide = hideFriendCompounds &&
         (QCString(md->typeString())=="friend class" || 
          QCString(md->typeString())=="friend struct" ||
@@ -716,10 +718,9 @@ static void addMemberToSearchIndex(
      )
   {
     QCString n = md->name();
-    uchar charCode = (uchar)n.at(0);
-    uint letter = charCode<128 ? tolower(charCode) : charCode;
     if (!n.isEmpty()) 
     {
+      uint letter = getNextUtf8OrToLower(n, 0);
       symbols[SEARCH_INDEX_ALL][letter].append(md);
       symbolCount[SEARCH_INDEX_ALL]++;
 
@@ -765,9 +766,20 @@ static QCString searchId(const QCString &s)
   for (i=0;i<s.length();i++)
   {
     c=s.at(i);
-    if ((c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z'))
+    if (c>0x7f || c<0)
+    {
+      result+=s.at(i);
+    }
+    else if (isalnum(c))
     {
-      result+=(char)tolower(c);
+      if (isupper(c))
+      {
+        result+=(char)tolower(c);
+      }
+      else
+      {
+        result+=s.at(i);
+      }
     }
     else
     {
@@ -780,7 +792,7 @@ static QCString searchId(const QCString &s)
 }
 
 static int g_searchIndexCount[NUM_SEARCH_INDICES];
-static SearchIndexList g_searchIndexSymbols[NUM_SEARCH_INDICES][MEMBER_INDEX_ENTRIES];
+static CodePointToSearchIndexList g_searchIndexSymbols[NUM_SEARCH_INDICES];
 static const char *g_searchIndexName[NUM_SEARCH_INDICES] = 
 { 
     "all",
@@ -834,8 +846,7 @@ void writeJavascriptSearchIndex()
   ClassDef *cd;
   for (;(cd=cli.current());++cli)
   {
-    uchar charCode = (uchar)cd->localName().at(0);
-    uint letter = charCode<128 ? tolower(charCode) : charCode;
+    uint letter = getNextUtf8OrToLower( cd->localName(), 0 );
     if (cd->isLinkable() && isId(letter))
     {
       g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(cd);
@@ -850,8 +861,7 @@ void writeJavascriptSearchIndex()
   NamespaceDef *nd;
   for (;(nd=nli.current());++nli)
   {
-    uchar charCode = (uchar)nd->name().at(0);
-    uint letter = charCode<128 ? tolower(charCode) : charCode;
+    uint letter = getNextUtf8OrToLower( nd->name(), 0 );
     if (nd->isLinkable() && isId(letter))
     {
       g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(nd);
@@ -870,8 +880,7 @@ void writeJavascriptSearchIndex()
     FileDef *fd;
     for (;(fd=fni.current());++fni)
     {
-      uchar charCode = (uchar)fd->name().at(0);
-      uint letter = charCode<128 ? tolower(charCode) : charCode;
+      uint letter = getNextUtf8OrToLower(fd->name(), 0);
       if (fd->isLinkable() && isId(letter))
       {
         g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(fd);
@@ -926,8 +935,7 @@ void writeJavascriptSearchIndex()
       QCString title = gd->groupTitle();
       if (!title.isEmpty()) // TODO: able searching for all word in the title
       {
-        uchar charCode = title.at(0);
-        uint letter = charCode<128 ? tolower(charCode) : charCode;
+        uint letter = getNextUtf8OrToLower(title, 0);
         if (isId(letter))
         {
           g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(gd);
@@ -949,8 +957,7 @@ void writeJavascriptSearchIndex()
       QCString title = pd->title();
       if (!title.isEmpty())
       {
-        uchar charCode = title.at(0);
-        uint letter = charCode<128 ? tolower(charCode) : charCode;
+        uint letter = getNextUtf8OrToLower(title, 0);
         if (isId(letter))
         {
           g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(pd);
@@ -966,8 +973,7 @@ void writeJavascriptSearchIndex()
     QCString title = Doxygen::mainPage->title();
     if (!title.isEmpty())
     {
-      uchar charCode = title.at(0);
-      uint letter = charCode<128 ? tolower(charCode) : charCode;
+      uint letter = getNextUtf8OrToLower(title, 0);
       if (isId(letter))
       {
         g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(Doxygen::mainPage);
@@ -977,29 +983,19 @@ void writeJavascriptSearchIndex()
       }
     }
   }
-  
-  // sort all lists
-  int i,p;
-  for (i=0;i<NUM_SEARCH_INDICES;i++)
-  {
-    for (p=0;p<MEMBER_INDEX_ENTRIES;p++)
-    {
-      if (g_searchIndexSymbols[i][p].count()>0)
-      {
-        g_searchIndexSymbols[i][p].sort();
-      }
-    }
-  }
 
   // write index files
   QCString searchDirName = Config_getString("HTML_OUTPUT")+"/search";
-
-  for (i=0;i<NUM_SEARCH_INDICES;i++)
+  int p;
+  for (int i=0;i<NUM_SEARCH_INDICES;i++)
   {
-    for (p=0;p<MEMBER_INDEX_ENTRIES;p++)
+    CodePointToSearchIndexList& li = g_searchIndexSymbols[i];
+    CodePointAndSearchIndexListPair* ptr;
+    for ( ptr = li.first() ; ptr ; ptr = li.next() )
     {
-      if (g_searchIndexSymbols[i][p].count()>0)
-      {
+        const uint p = ptr->getFirst();
+        SearchIndexList* ptrSecond = ptr->getSecond();
+        ptrSecond->sort(); // SDict doesn't sort automatically. we do here.
         QCString baseName;
         baseName.sprintf("%s_%02x",g_searchIndexName[i],p);
 
@@ -1060,7 +1056,7 @@ void writeJavascriptSearchIndex()
           ti << "[" << endl;
           bool firstEntry=TRUE;
 
-          SDict<QList<Definition> >::Iterator li(g_searchIndexSymbols[i][p]);
+          SDict<QList<Definition> >::Iterator li(*ptrSecond);
           QList<Definition> *dl;
           int itemCount=0;
           for (li.toFirst();(dl=li.current());++li)
@@ -1183,7 +1179,7 @@ void writeJavascriptSearchIndex()
                 }
                 else if (md) // unique member function
                 {
-                  prefix+="()"; // only to show it is a function
+                  prefix+="()"; // only to show ptr is a function
                 }
                 QCString name;
                 if (d->definitionType()==Definition::TypeClass)
@@ -1251,7 +1247,6 @@ void writeJavascriptSearchIndex()
         {
           err("Failed to open file '%s' for writing...\n",fileName.data());
         }
-      }
     }
   }
 
@@ -1269,15 +1264,18 @@ void writeJavascriptSearchIndex()
       t << "{" << endl;
       bool first=TRUE;
       int j=0;
-      for (i=0;i<NUM_SEARCH_INDICES;i++)
+      for (int i=0;i<NUM_SEARCH_INDICES;i++)
       {
         if (g_searchIndexCount[i]>0)
         {
           if (!first) t << "," << endl;
           t << "  " << j << ": \"";
-          for (p=0;p<MEMBER_INDEX_ENTRIES;p++)
+  
+          CodePointToSearchIndexList& li = g_searchIndexSymbols[i];
+          CodePointAndSearchIndexListPair* ptr;
+          for ( ptr = li.first() ; ptr ; ptr = li.next() )
           {
-            t << (g_searchIndexSymbols[i][p].count()>0 ? "1" : "0");
+              t << QString( QChar( ptr->getFirst() ) ).utf8();
           }
           t << "\"";
           first=FALSE;
@@ -1290,7 +1288,7 @@ void writeJavascriptSearchIndex()
       t << "{" << endl;
       first=TRUE;
       j=0;
-      for (i=0;i<NUM_SEARCH_INDICES;i++)
+      for (int i=0;i<NUM_SEARCH_INDICES;i++)
       {
         if (g_searchIndexCount[i]>0)
         {
diff --git a/src/util.cpp b/src/util.cpp
index e67e9fb..1422a9d 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -6964,6 +6964,79 @@ QCString parseCommentAsText(const Definition *scope,const MemberDef *md,
 
 //--------------------------------------------------------------------------------------
 
+/*! @brief Pick one unicode character as interger from utf-8 string
+
+Called from index.cpp and searchindex.cpp through
+getNextUtf8OrToLower() or getNextUtf8OrToUpper().
+
+@param s utf-8 string
+@param idx pick up position of given string \a s.
+@return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT
+@see getNextUtf8OrToLower()
+@see getNextUtf8OrToUpper()
+*/
+uint getNextUtf8Code( const QCString& s, int idx )
+{
+  const int length = s.length();
+  if (idx >= length) { return 0; }
+  const uint c0 = (uchar)s.at(idx);
+  if ( c0 < 0xC2 || c0 >= 0xF8 )
+  {
+    return c0;
+  }
+  if (idx+1 >= length) { return 0; }
+  const uint c1 = ((uchar)s.at(idx+1)) & 0x3f;
+  if ( c0 < 0xE0 )
+  {
+    return ((c0 & 0x1f) << 6) | c1;
+  }
+  if (idx+2 >= length) { return 0; }
+  const uint c2 = ((uchar)s.at(idx+2)) & 0x3f;
+  if ( c0 < 0xF0 )
+  {
+    return ((c0 & 0x0f) << 12) | (c1 << 6) | c2;
+  }
+  if (idx+3 >= length) { return 0; }
+  const uint c3 = ((uchar)s.at(idx+3)) & 0x3f;
+  return ((c0 & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
+}
+
+
+/*! @brief Pick one unicode character as interger from utf-8 string
+
+Called from index.cpp and searchindex.cpp
+
+@param s utf-8 string
+@param idx pick up position of given string \a s.
+@return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT, excludes 'A'-'Z'
+@see getNextUtf8Code()
+@see getNextUtf8OrToUpper()
+*/
+uint getNextUtf8OrToLower( const QCString& s, int idx )
+{
+  const uint v = getNextUtf8Code( s, idx );
+  return v < 0x7f ? tolower( v ) : v;
+}
+
+
+/*! @brief Pick one unicode character as interger from utf-8 string
+
+Called from index.cpp.
+
+@param s utf-8 string
+@param idx pick up position of given string \a s.
+@return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT, excludes 'a'-'z'
+@see getNextUtf8Code()
+@see getNextUtf8OrToUpper()
+*/
+uint getNextUtf8OrToUpper( const QCString& s, int idx )
+{
+  const uint v = getNextUtf8Code( s, idx );
+  return v < 0x7f ? toupper( v ) : v;
+}
+
+//--------------------------------------------------------------------------------------
+
 static QDict<void> aliasesProcessed;
 
 static QCString expandAliasRec(const QCString s);
diff --git a/src/util.h b/src/util.h
index 1aba005..2f4ea4b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -383,6 +383,14 @@ QCString externalRef(const QCString &relPath,const QCString &ref,bool href);
 int nextUtf8CharPosition(const QCString &utf8Str,int len,int startPos);
 const char *writeUtf8Char(FTextStream &t,const char *s);
 
+uint getNextUtf8Code( const QCString& s, int idx );
+uint getNextUtf8OrToLower( const QCString& s, int idx );
+uint getNextUtf8OrToUpper( const QCString& s, int idx );
+
+#ifndef MAX_UNICODE_CODEPOINT
+#define MAX_UNICODE_CODEPOINT (0x10FFFF)
+#endif
+
 /** Data associated with a HSV colored image. */
 struct ColoredImgDataItem
 {
