 src/index.cpp | 169 +++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 121 insertions(+), 48 deletions(-)

diff --git a/src/index.cpp b/src/index.cpp
index ae96c80..5a7251d 100644
--- a/src/index.cpp
+++ b/src/index.cpp
@@ -1627,41 +1627,116 @@ static void writeAnnotatedClassList(OutputList &ol)
   ol.endIndexList();
 }
 
-static QCString letterToLabel(uint startLetter)
+bool isUsableOnURL(const uint codePoint)
 {
-  char s[10];
-  if (startLetter>0x20 && startLetter<=0x7f) // printable ASCII character
+  // 'unUsable' below includes reserved characters defined RFC2346
+  // and those cannot be used as a part of filename.
+  const QCString unUsable(" \\\"'|*`^><)(}{][;/?:@&=+$,");
+  if (codePoint<0x80 && unUsable.find((char)codePoint)!=-1)
+    return false;
+  return codePoint>=0x20;
+}
+
+/** \brief generate the header string as the label
+
+\sa getEscapedAnchor
+\sa getEscapedFilenameAsURL
+
+Consider carefully what we should do. The caller of this function
+should take care to where the returned value is applied.
+
+As URL, we can use non-ASCII characters, because the HTML output
+of doxygen do NOT send URLs directly to network, as the user
+agents (web browsers) will do escape URLs. But on the other hand,
+we should escape 'reserved' characters defined with RFC2396.
+
+As Filename, we should escape some characters especially NTFS,
+used by MS-Windows.
+
+As label text, we can use all printable characters. Is there 
+any probably that white-space characters are used as the head
+of IDs?
+*/
+static QCString getEscapedLabel(const uint startLetter, const bool makeUpper = true)
+{
+  if (startLetter>=0xa0 || isprint((int)startLetter))
   {
-    s[0]=(char)startLetter;
-    s[1]=0;
+    if (makeUpper)
+    {
+      return QString( QChar(startLetter) ).upper().utf8();
+    }
+    else
+    {
+      return QString( QChar(startLetter) ).lower().utf8();
+    }
   }
   else
   {
+    char s[5];
     const char hex[]="0123456789abcdef";
-    int i=0;
-    s[i++]='0';
-    s[i++]='x';
-    if (startLetter>(1<<24)) // 4 byte character
-    {
-      s[i++]=hex[(startLetter>>28)&0xf];
-      s[i++]=hex[(startLetter>>24)&0xf];
-    }
-    if (startLetter>(1<<16)) // 3 byte character
+    s[0]='0';
+    s[1]='x';
+    s[2]=hex[startLetter>>4];
+    s[3]=hex[startLetter&0xF];
+    s[4]=0;
+    return s;
+  }
+}
+
+/** \brief generate the header string as the anchor of URL
+
+\sa getEscapedLabel
+\sa getEscapedFilenameAsURL
+*/
+static QCString getEscapedAnchor(const uint startLetter)
+{
+  // reserved as the separators in RFC2396 and add '#'
+  const QCString unUsable(";/?:@&=+$,#");
+  if (startLetter < 0x80 && unUsable.find((char)startLetter) != -1)
+  {
+    QCString s;
+    s.sprintf("0x%02x",startLetter);
+    return s;
+  }
+  else
+  {
+    return getEscapedLabel(startLetter);
+  }
+}
+
+/** \brief generate the header string commonly usable as the filename and the URL
+
+\sa getEscapedLabel
+\sa getEscapedAnchor
+*/
+static QCString getEscapedFilenameAsURL(uint startLetter)
+{
+  const QCString unUsablesWithFilename("\\/:*?\"<>|");
+  if (startLetter < 0x80)
+  {
+    if (unUsablesWithFilename.find((char)startLetter) != -1)
     {
-      s[i++]=hex[(startLetter>>20)&0xf];
-      s[i++]=hex[(startLetter>>16)&0xf];
+      QCString s;
+      s.sprintf("0x%02x",startLetter);
+      return s;
     }
-    if (startLetter>(1<<8)) // 2 byte character
+    else
     {
-      s[i++]=hex[(startLetter>>12)&0xf];
-      s[i++]=hex[(startLetter>>8)&0xf];
+      char s[2];
+      s[0]=(char)startLetter; s[1]=0;
+      return s;
     }
-    // one byte character
-    s[i++]=hex[(startLetter>>4)&0xf];
-    s[i++]=hex[(startLetter>>0)&0xf];
-    s[i++]=0;
   }
-  return s;
+  else
+  {
+    QCString s;
+    const char* format;
+    if (startLetter>(1<<16)) format = "0x%06x";
+    else if (startLetter>(1<<8)) format = "0x%04x";
+    else format = "0x%02x";
+    s.sprintf(format,startLetter);
+    return s;
+  }
 }
 
 //----------------------------------------------------------------------------
@@ -1784,11 +1859,11 @@ static void writeAlphabeticalClassList(OutputList &ol)
   {
     if (headerItems) alphaLinks += "&#160;|&#160;";
     headerItems++;
-    QCString li = letterToLabel(*pLetter);
-    QCString ls = QString(QChar(*pLetter)).utf8();
+    QCString anchor = getEscapedAnchor(*pLetter);
+    QCString label = getEscapedLabel(*pLetter);
     alphaLinks += (QCString)"<a class=\"qindex\" href=\"#letter_" + 
-                  li + "\">" + 
-                  ls + "</a>";
+                  anchor + "\">" + 
+                  label + "</a>";
   }
   alphaLinks += "</div>\n";
   ol.writeString(alphaLinks);
@@ -1821,7 +1896,7 @@ static void writeAlphabeticalClassList(OutputList &ol)
     if (cd->isLinkableInProject() && cd->templateMaster()==0)
     {
       int index = getPrefixIndex(cd->className());
-      startLetter=getUtf8Code(cd->className(),index);
+      startLetter=getUtf8CodeToUpper(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)
@@ -1916,15 +1991,14 @@ static void writeAlphabeticalClassList(OutputList &ol)
           {
             if (cell->letter()!=0)
             {
-              QCString s = letterToLabel(cell->letter());
               ol.writeString("<td rowspan=\"2\" valign=\"bottom\">");
-              ol.writeString("<a name=\"letter_");
-              ol.writeString(s);
+              ol.writeString("<a id=\"letter_");
+              ol.writeString(getEscapedAnchor(cell->letter()));
               ol.writeString("\"></a>");
               ol.writeString("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
                   "<tr>"
                   "<td><div class=\"ah\">&#160;&#160;"); 
-              ol.writeString(QString(QChar(cell->letter())).utf8());
+              ol.writeString(getEscapedLabel(cell->letter()));
               ol.writeString(         "&#160;&#160;</div>"
                   "</td>"
                   "</tr>"
@@ -2169,6 +2243,7 @@ static void writeMemberList(OutputList &ol,bool useSections,int page,
   };
   QCString prevName;
   QCString prevDefName;
+  uint currentSectionChar = 0;
   bool first=TRUE;
   bool firstSection=TRUE;
   bool firstItem=TRUE;
@@ -2194,22 +2269,20 @@ static void writeMemberList(OutputList &ol,bool useSections,int page,
       int startIndex = getPrefixIndex(name);
       if (QCString(name.data()+startIndex)!=prevName) // new entry
       {
-        if ((prevName.isEmpty() || 
-            tolower(name.at(startIndex))!=tolower(prevName.at(0))) && 
-            useSections) // new section
+        const uint newSectionChar = getUtf8CodeToLower(name,startIndex);
+        if (useSections && newSectionChar!=currentSectionChar) // new section
         {
           if (!firstItem)    ol.endItemListItem();
           if (!firstSection) ol.endItemList();
-          QCString cs = letterToLabel(ml->letter());
-          QCString cl = QString(QChar(ml->letter())).utf8();
-          QCString anchor=(QCString)"index_"+cs;
-          QCString title=(QCString)"- "+cl+" -";
+          QCString anchor=(QCString)"index_"+getEscapedAnchor(ml->letter());
+          QCString title=(QCString)"- "+getEscapedLabel(ml->letter(),false)+" -";
           ol.startSection(anchor,title,SectionInfo::Subsection);
           ol.docify(title);
           ol.endSection(anchor,SectionInfo::Subsection);
           ol.startItemList();
           firstSection=FALSE;
           firstItem=TRUE;
+          currentSectionChar = newSectionChar;
         }
         else if (!useSections && first)
         {
@@ -2281,7 +2354,7 @@ void addClassMemberNameToIndex(MemberDef *md)
         g_memberIndexLetterUsed[CMHL_All].append(letter,md);
         documentedClassMembers[CMHL_All]++;
       }
-      if (md->isFunction()  || md->isSlot() || md->isSignal())
+      if (md->isFunction() || md->isSlot() || md->isSignal())
       {
         g_memberIndexLetterUsed[CMHL_Functions].append(letter,md);
         documentedClassMembers[CMHL_Functions]++;
@@ -2452,8 +2525,8 @@ static void writeQuickMemberIndex(OutputList &ol,
   for (it.toFirst();(ml=it.current());++it)
   {
     uint i = ml->letter();
-    QCString is = letterToLabel(i);
-    QCString ci = QString(QChar(i)).utf8();
+    QCString ci = getEscapedAnchor(i);
+    QCString cl = getEscapedLabel(i,false);
     QCString anchor;
     QCString extension=Doxygen::htmlFileExtension;
     if (!multiPage)
@@ -2461,9 +2534,9 @@ static void writeQuickMemberIndex(OutputList &ol,
     else if (first) 
       anchor=fullName+extension+"#index_";
     else 
-      anchor=fullName+"_"+letterToLabel(i)+extension+"#index_";
+      anchor=fullName+"_"+getEscapedFilenameAsURL(i)+extension+"#index_";
     startQuickIndexItem(ol,anchor+ci,i==page,TRUE,first);
-    ol.writeString(is);
+    ol.writeString(cl);
     endQuickIndexItem(ol);
     first=FALSE;
   }
@@ -2541,7 +2614,7 @@ static void writeClassMemberIndexFiltered(OutputList &ol, ClassMemberHighlight h
     { 
       if (!first)
       {
-        fileName+="_"+letterToLabel(page);
+        fileName+="_"+getEscapedFilenameAsURL(page);
       }
       QCString cs = QString(QChar(page)).utf8();
       if (addToIndex)
@@ -2713,7 +2786,7 @@ static void writeFileMemberIndexFiltered(OutputList &ol, FileMemberHighlight hl)
     {
       if (!first)
       {
-        fileName+="_"+letterToLabel(page);
+        fileName+="_"+getEscapedFilenameAsURL(page);
       }
       QCString cs = QString(QChar(page)).utf8();
       if (addToIndex)
@@ -2881,7 +2954,7 @@ static void writeNamespaceMemberIndexFiltered(OutputList &ol,
     {
       if (!first)
       {
-        fileName+="_"+letterToLabel(page);
+        fileName+="_"+getEscapedFilenameAsURL(page);
       }
       QCString cs = QString(QChar(page)).utf8();
       if (addToIndex)
