瀏覽代碼

ENH: Re-implemented document generation class to be more organized and more robust.

Brad King 23 年之前
父節點
當前提交
5e18bec8f7
共有 4 個文件被更改,包括 590 次插入368 次删除
  1. 12 7
      Source/CursesDialog/ccmake.cxx
  2. 470 319
      Source/cmDocumentation.cxx
  3. 99 33
      Source/cmDocumentation.h
  4. 9 9
      Source/cmakemain.cxx

+ 12 - 7
Source/CursesDialog/ccmake.cxx

@@ -30,8 +30,8 @@
 //----------------------------------------------------------------------------
 static const cmDocumentationEntry cmDocumentationName[] =
 {
-  {"ccmake",
-   "- Curses Interface for CMake.", 0},
+  {0,
+   "  ccmake - Curses Interface for CMake.", 0},
   {0,0,0}
 };
 
@@ -39,7 +39,7 @@ static const cmDocumentationEntry cmDocumentationName[] =
 static const cmDocumentationEntry cmDocumentationUsage[] =
 {
   {0,
-   "ccmake <path-to-source>", 0},
+   "  ccmake <path-to-source>", 0},
   {0,0,0}
 };
 
@@ -88,10 +88,15 @@ int main(int argc, char** argv)
   cmDocumentation doc;
   if(cmDocumentation::Type ht = doc.CheckOptions(argc, argv))
     {
-    doc.SetName(cmDocumentationName);
-    doc.SetUsage(cmDocumentationUsage);
-    doc.SetDescription(cmDocumentationDescription);
-    doc.Print(ht, std::cout);
+    cmake hcm;
+    std::vector<cmDocumentationEntry> commands;
+    hcm.GetCommandDocumentation(commands);
+    doc.SetNameSection(cmDocumentationName);
+    doc.SetUsageSection(cmDocumentationUsage);
+    doc.SetDescriptionSection(cmDocumentationDescription);
+    doc.SetOptionsSection(0);
+    doc.SetCommandsSection(&commands[0]);
+    doc.PrintDocumentation(ht, std::cout);
     return 0;
     }  
   

+ 470 - 319
Source/cmDocumentation.cxx

@@ -49,37 +49,45 @@ const cmDocumentationEntry cmDocumentationMailingList[] =
 {
   {0,
    "For help and discussion about using cmake, a mailing list is provided "
-   "at [email protected]. Please first read the full documentation at "
+   "at [email protected].  Please first read the full documentation at "
    "http://www.cmake.org before posting questions to the list.", 0},
   {0,0,0}
 };
 
+//----------------------------------------------------------------------------
+const cmDocumentationEntry cmDocumentationAuthor[] =
+{
+  {0,
+   "This manual page was generated by \"cmake --help-man\".", 0},
+  {0,0,0}
+};
+
 //----------------------------------------------------------------------------
 const cmDocumentationEntry cmDocumentationCopyright[] =
 {
   {0,
-   "Copyright (c) 2002 Kitware, Inc., Insight Consortium.\n"
-   "All rights reserved.\n", 0},
+   "Copyright (c) 2002 Kitware, Inc., Insight Consortium.  "
+   "All rights reserved.", 0},
   {0,
    "Redistribution and use in source and binary forms, with or without "
    "modification, are permitted provided that the following conditions are "
-   "met:\n", 0},
-  {" * ",
+   "met:", 0},
+  {"",
    "Redistributions of source code must retain the above copyright notice, "
-   "this list of conditions and the following disclaimer.\n", 0},
-  {" * ",
+   "this list of conditions and the following disclaimer.", 0},
+  {"",
    "Redistributions in binary form must reproduce the above copyright "
    "notice, this list of conditions and the following disclaimer in the "
-   "documentation and/or other materials provided with the distribution.\n",
+   "documentation and/or other materials provided with the distribution.",
    0},
-  {" * ",
+  {"",
    "The names of Kitware, Inc., the Insight Consortium, or the names of "
    "any consortium members, or of any contributors, may not be used to "
    "endorse or promote products derived from this software without "
-   "specific prior written permission.\n", 0},
-  {" * ",
+   "specific prior written permission.", 0},
+  {"",
    "Modified source versions must be plainly marked as such, and must "
-   "not be misrepresented as being the original software.\n", 0},
+   "not be misrepresented as being the original software.", 0},
   {0,
    "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "
    "``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "
@@ -91,124 +99,218 @@ const cmDocumentationEntry cmDocumentationCopyright[] =
    "PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF "
    "LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING "
    "NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS "
-   "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n", 0},
+   "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", 0},
   {0, 0, 0}
 };
 
 //----------------------------------------------------------------------------
 cmDocumentation::cmDocumentation()
 {
-  this->SetCommands(0);
-  this->Description = 0;
-  this->Name = 0;
-  this->UsageHelp = 0;  
-  this->SetOptions(0);
+  this->CurrentForm = TextForm;
+  this->TextIndent = "";
+  this->TextWidth = 77;
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintManSection(std::ostream& os,
-                                      const cmDocumentationEntry* section,
-                                      const char* name)
+void cmDocumentation::PrintCopyright(std::ostream& os)
 {
-  if(!section) { return; }
-  os << ".SH " << name << "\n";
-  for(const cmDocumentationEntry* op = section; op->brief; ++op)
+  os << "CMake version " CMake_VERSION_STRING "\n";
+  for(const cmDocumentationEntry* op = cmDocumentationCopyright;
+      op->brief; ++op)
     {
     if(op->name)
       {
-      os << ".TP\n"
-         << ".B " << op->name << "\n"
-         << op->brief << "\n\n";
-      if(op->full)
-        {
-        this->PrintFull(os, op->full, 0, 0);
-        }
+      os << " * ";
+      this->TextIndent = "    ";
+      this->PrintColumn(os, op->brief);
       }
     else
       {
-      os << ".PP\n"
-         << op->brief << "\n";
+      this->TextIndent = "";
+      this->PrintColumn(os, op->brief);
       }
-    }  
+    os << "\n";
+    }
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintHelpSection(std::ostream& os,
-                                       const cmDocumentationEntry* section)
+void cmDocumentation::PrintVersion(std::ostream& os)
 {
-  if(!section) { return; }
-  for(const cmDocumentationEntry* op = section; op->brief; ++op)
+  os << "CMake version " CMake_VERSION_STRING "\n";
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::AddSection(const char* name,
+                                 const cmDocumentationEntry* d)
+{
+  this->Names.push_back(name);
+  this->Sections.push_back(d);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::ClearSections()
+{
+  this->Names.erase(this->Names.begin(), this->Names.end());
+  this->Sections.erase(this->Sections.begin(), this->Sections.end());
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintDocumentation(Type ht, std::ostream& os)
+{
+  switch (ht)
     {
-    if(op->name)
-      {
-      os << "  " << op->name << "\n"
-         << "       ";
-      this->PrintColumn(os, 70, "       ", op->brief);
-      if(op->full)
-        {
-        os << "\n"
-           << "\n"
-           << "       ";
-        this->PrintColumn(os, 70, "       ", op->full);
-        }
-      os << "\n";
-      }
-    else
-      {
-      this->PrintColumn(os, 77, "", op->brief);
-      os << "\n";
-      }
-    os << "\n";
-    }  
+    case cmDocumentation::Usage:     this->PrintDocumentationUsage(os); break;
+    case cmDocumentation::Full:      this->PrintDocumentationFull(os); break;
+    case cmDocumentation::HTML:      this->PrintDocumentationHTML(os); break;
+    case cmDocumentation::Man:       this->PrintDocumentationMan(os); break;
+    case cmDocumentation::Copyright: this->PrintCopyright(os); break;
+    case cmDocumentation::Version:   this->PrintVersion(os); break;
+    default: break;
+    }
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintHTMLEscapes(std::ostream& os, const char* text)
+cmDocumentation::Type cmDocumentation::CheckOptions(int argc, char** argv)
 {
-  static cmDocumentationEntry escapes[] =
-  {
-    {"<", "&lt;", 0},
-    {">", "&gt;", 0},
-    {"&", "&amp;", 0},
-    {"\n", "<br>", 0},
-    {0,0,0}
-  };
-  for(const char* p = text; *p; ++p)
+  for(int i=1; i < argc; ++i)
     {
-    bool found = false;
-    for(const cmDocumentationEntry* op = escapes; !found && op->name; ++op)
+    if((strcmp(argv[i], "-help") == 0) ||
+       (strcmp(argv[i], "--help") == 0) ||
+       (strcmp(argv[i], "/?") == 0) ||
+       (strcmp(argv[i], "-usage") == 0) ||
+       (strcmp(argv[i], "-h") == 0) ||
+       (strcmp(argv[i], "-H") == 0))
       {
-      if(op->name[0] == *p)
-        {
-        os << op->brief;
-        found = true;
-        }
+      return cmDocumentation::Usage;
       }
-    if(!found)
+    if(strcmp(argv[i], "--help-full") == 0)
       {
-      os << *p;
+      return cmDocumentation::Full;
+      }
+    if(strcmp(argv[i], "--help-html") == 0)
+      {
+      return cmDocumentation::HTML;
+      }
+    if(strcmp(argv[i], "--help-man") == 0)
+      {
+      return cmDocumentation::Man;
+      }
+    if(strcmp(argv[i], "--copyright") == 0)
+      {
+      return cmDocumentation::Copyright;
+      }
+    if((strcmp(argv[i], "--version") == 0) || 
+       (strcmp(argv[i], "-version") == 0) || 
+       (strcmp(argv[i], "-V") == 0) || 
+       (strcmp(argv[i], "/V") == 0))
+      {
+      return cmDocumentation::Version;
       }
     }
+  return cmDocumentation::None;
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintHTMLPreformatted(std::ostream& os, const char* text)
+void cmDocumentation::Print(Form f, std::ostream& os)
 {
-  os << "<pre>";
-  cmDocumentation::PrintHTMLEscapes(os, text);
-  os << "</pre>";
+  this->CurrentForm = f;
+  for(int i=0; i < this->Sections.size(); ++i)
+    {
+    this->PrintSection(os, this->Sections[i], this->Names[i]);
+    }
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::SetNameSection(const cmDocumentationEntry* section)
+{
+  this->SetSection(0, section, 0, this->NameSection);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::SetUsageSection(const cmDocumentationEntry* section)
+{
+  this->SetSection(0, section, 0, this->UsageSection);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::SetDescriptionSection(const cmDocumentationEntry* section)
+{
+  this->SetSection(0, section, 0, this->DescriptionSection);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::SetOptionsSection(const cmDocumentationEntry* section)
+{
+  this->SetSection(0, section, cmDocumentationStandardOptions,
+                   this->OptionsSection);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::SetCommandsSection(const cmDocumentationEntry* section)
+{
+  this->SetSection(cmDocumentationCommandsHeader, section, 0,
+                   this->CommandsSection);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintSection(std::ostream& os,
+                                   const cmDocumentationEntry* section,
+                                   const char* name)
+{
+  switch (this->CurrentForm)
+    {
+    case TextForm: this->PrintSectionText(os, section, name); break;
+    case HTMLForm: this->PrintSectionHTML(os, section, name); break;
+    case ManForm: this->PrintSectionMan(os, section, name); break;
+    case UsageForm: this->PrintSectionUsage(os, section, name); break;
+    }
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintHelpHTMLSection(std::ostream& os,
-                                           const cmDocumentationEntry* section,
-                                           const char* header)
+void cmDocumentation::PrintSectionText(std::ostream& os,
+                                       const cmDocumentationEntry* section,
+                                       const char* name)
 {
+  if(name)
+    {
+    os << name << "\n\n";
+    }
   if(!section) { return; }
-  if(header)
+  for(const cmDocumentationEntry* op = section; op->brief; ++op)
     {
-    os << "<h2>" << header << "</h2>\n";
+    if(op->name)
+      {
+      if(op->name[0])
+        {
+        os << "  " << op->name << "\n";
+        }
+      this->TextIndent = "       ";
+      this->PrintFormatted(os, op->brief);
+      if(op->full)
+        {
+        os << "\n";
+        this->PrintFormatted(os, op->full);
+        }
+      }
+    else
+      {
+      this->TextIndent = "";
+      this->PrintFormatted(os, op->brief);
+      }
+    os << "\n";
+    }  
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintSectionHTML(std::ostream& os,
+                                       const cmDocumentationEntry* section,
+                                       const char* name)
+{
+  if(name)
+    {
+    os << "<h2>" << name << "</h2>\n";
     }
+  if(!section) { return; }
   for(const cmDocumentationEntry* op = section; op->brief;)
     {
     if(op->name)
@@ -217,16 +319,17 @@ void cmDocumentation::PrintHelpHTMLSection(std::ostream& os,
       for(;op->name;++op)
         {
         os << "  <li>\n";
-        os << "    <b><code>";
-        this->PrintHTMLEscapes(os, op->name);
-        os << "</code></b>: ";
+        if(op->name[0])
+          {
+          os << "    <b><code>";
+          this->PrintHTMLEscapes(os, op->name);
+          os << "</code></b>: ";
+          }
         this->PrintHTMLEscapes(os, op->brief);
         if(op->full)
           {
-          os << "<br>";
-          this->PrintFull(os, op->full,
-                          &cmDocumentation::PrintHTMLPreformatted,
-                          &cmDocumentation::PrintHTMLEscapes);
+          os << "<br>\n    ";
+          this->PrintFormatted(os, op->full);
           }
         os << "\n";
         os << "  </li>\n";
@@ -235,7 +338,7 @@ void cmDocumentation::PrintHelpHTMLSection(std::ostream& os,
       }
     else
       {
-      this->PrintHTMLEscapes(os, op->brief);
+      this->PrintFormatted(os, op->brief);
       os << "\n";
       ++op;
       }
@@ -243,136 +346,202 @@ void cmDocumentation::PrintHelpHTMLSection(std::ostream& os,
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintUsageSection(std::ostream& os,
-                                        const cmDocumentationEntry* section)
+void cmDocumentation::PrintSectionMan(std::ostream& os,
+                                      const cmDocumentationEntry* section,
+                                      const char* name)
 {
+  if(name)
+    {
+    os << ".SH " << name << "\n";
+    }
+  if(!section) { return; }
+  for(const cmDocumentationEntry* op = section; op->brief; ++op)
+    {
+    if(op->name)
+      {
+      os << ".TP\n"
+         << ".B " << (op->name[0]?op->name:"*") << "\n";
+      this->PrintFormatted(os, op->brief);
+      this->PrintFormatted(os, op->full);
+      }
+    else
+      {
+      os << ".PP\n";
+      this->PrintFormatted(os, op->brief);
+      }
+    }  
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintSectionUsage(std::ostream& os,
+                                        const cmDocumentationEntry* section,
+                                        const char* name)
+{
+  if(name)
+    {
+    os << name << "\n";
+    }
   if(!section) { return; }
   for(const cmDocumentationEntry* op = section; op->brief; ++op)
     {
     if(op->name)
       {
       os << "  " << op->name;
-      for(int i = static_cast<int>(strlen(op->name)); i < 25; ++i)
+      this->TextIndent = "                             ";
+      int align = static_cast<int>(strlen(this->TextIndent))-4;
+      for(int i = static_cast<int>(strlen(op->name)); i < align; ++i)
         {
         os << " ";
         }
-      os << "= " << op->brief << "\n";
+      os << "= ";
+      this->PrintColumn(os, op->brief);
+      os << "\n";
       }
     else
       {
       os << "\n";
-      this->PrintColumn(os, 74, "", op->brief);
+      this->TextIndent = "";
+      this->PrintFormatted(os, op->brief);
       os << "\n";
       }
-    }  
-}
-
-//----------------------------------------------------------------------------
-void cmDocumentation::PrintUsage(std::ostream& os)
-{
-  os << "Usage:\n";
-  this->PrintUsageSection(os, this->UsageHelp);
-  this->PrintUsageSection(os, &this->Options[0]);
+    }
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintHelp(std::ostream& os)
+void cmDocumentation::PrintFormatted(std::ostream& os, const char* text)
 {
-  os << "Usage:\n";
-  os << "\n";
-  this->PrintHelpSection(os, this->UsageHelp);
-  this->PrintHelpSection(os, this->Description);
-  os << "--------------------------------------------------------------------------\n";
-  this->PrintHelpSection(os, &this->Options[0]);
-  os << "--------------------------------------------------------------------------\n";
-  this->PrintHelpSection(os, &this->Commands[0]);
+  if(!text)
+    {
+    return;
+    }
+  const char* ptr = text;
+  while(*ptr)
+    {
+    // Any ptrs starting in a space are treated as preformatted text.
+    std::string preformatted;
+    while(*ptr == ' ')
+      {
+      for(char ch = *ptr; ch && ch != '\n'; ++ptr, ch = *ptr)
+        {
+        preformatted.append(1, ch);
+        }
+      if(*ptr)
+        {
+        ++ptr;
+        preformatted.append(1, '\n');
+        }
+      }
+    if(preformatted.length())
+      {
+      this->PrintPreformatted(os, preformatted.c_str());
+      }
+    
+    // Other ptrs are treated as paragraphs.
+    std::string paragraph;
+    for(char ch = *ptr; ch && ch != '\n'; ++ptr, ch = *ptr)
+      {
+      paragraph.append(1, ch);
+      }
+    if(*ptr)
+      {
+      ++ptr;
+      paragraph.append(1, '\n');
+      }
+    if(paragraph.length())
+      {
+      this->PrintParagraph(os, paragraph.c_str());
+      }
+    }
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintHelpHTML(std::ostream& os)
+void cmDocumentation::PrintPreformatted(std::ostream& os, const char* text)
 {
-  os << "<html>\n"
-     << "<body>\n";
-  os << "<h2>Using CMake</h2>\n";
-  if(this->UsageHelp)
+  switch (this->CurrentForm)
     {
-    os << "<blockquote><code>\n";
-    this->PrintHelpHTMLSection(os, this->UsageHelp, 0);
-    os << "</code></blockquote>\n";
+    case TextForm: this->PrintPreformattedText(os, text); break;
+    case HTMLForm: this->PrintPreformattedHTML(os, text); break;
+    case ManForm: this->PrintPreformattedMan(os, text); break;
+    case UsageForm: this->PrintPreformattedText(os, text); break;
     }
-  this->PrintHelpHTMLSection(os, this->Description, 0);
-  this->PrintHelpHTMLSection(os, &this->Options[0], "Command-line Options");
-  this->PrintHelpHTMLSection(os, &this->Commands[0], "Listfile Commands");
-  //this->PrintHelpHTMLSection(os, cmDocumentationCopyright, "Copyright");
-  //this->PrintHelpHTMLSection(os, cmDocumentationMailingList, "Mailing List");
-  os << "</body>\n"
-     << "</html>\n";
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintManPage(std::ostream& os)
+void cmDocumentation::PrintParagraph(std::ostream& os, const char* text)
 {
-  os << ".TH CMake 1 \""
-     << cmSystemTools::GetCurrentDateTime("%B %d, %Y").c_str()
-     << "\" \"CMake " CMake_VERSION_STRING "\"\n";
-  this->PrintManSection(os, this->Name, "NAME");
-  this->PrintManSection(os, this->UsageHelp, "SYNOPSIS");
-  this->PrintManSection(os, this->Description, "DESCRIPTION");
-  this->PrintManSection(os, &this->Options[0], "OPTIONS");
-  this->PrintManSection(os, &this->Commands[0], "COMMANDS");
-  this->PrintManSection(os, cmDocumentationCopyright, "COPYRIGHT");
-  os << ".SH MAILING LIST\n";
-  os << "For help and discussion about using cmake, a mailing list is\n"
-     << "provided at\n"
-     << ".B [email protected].\n"
-     << "Please first read the full documentation at\n"
-     << ".B http://www.cmake.org\n"
-     << "before posting questions to the list.\n";
-  os << ".SH AUTHOR\n"
-     << "This manual page was generated by \"cmake --help-man\".\n";
+  switch (this->CurrentForm)
+    {
+    case TextForm: this->PrintParagraphText(os, text); break;
+    case HTMLForm: this->PrintParagraphHTML(os, text); break;
+    case ManForm: this->PrintParagraphMan(os, text); break;
+    case UsageForm: this->PrintParagraphText(os, text); break;
+    }
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintCopyright(std::ostream& os)
+void cmDocumentation::PrintPreformattedText(std::ostream& os, const char* text)
 {
-  os << "CMake version " CMake_VERSION_STRING "\n";
-  for(const cmDocumentationEntry* op = cmDocumentationCopyright;
-      op->brief; ++op)
+  bool newline = true;
+  for(const char* ptr = text; *ptr; ++ptr)
     {
-    if(op->name)
+    if(newline)
       {
-      os << " * ";
-      this->PrintColumn(os, 74, "   ", op->brief);
+      os << this->TextIndent;
+      newline = false;
       }
-    else
+    os << *ptr;
+    if(*ptr == '\n')
       {
-      this->PrintColumn(os, 77, "", op->brief);
+      newline = true;
       }
-    os << "\n";
     }
+  os << "\n";
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintVersion(std::ostream& os)
+void cmDocumentation::PrintParagraphText(std::ostream& os, const char* text)
 {
-  os << "CMake version " CMake_VERSION_STRING "\n";
+  os << this->TextIndent;
+  this->PrintColumn(os, text);
+  os << "\n";
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintPreformattedHTML(std::ostream& os, const char* text)
+{
+  os << "<pre>";
+  this->PrintHTMLEscapes(os, text);
+  os << "</pre>\n    ";
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintColumn(std::ostream& os, int width,
-                                  const char* indent, const char* text)
+void cmDocumentation::PrintParagraphHTML(std::ostream& os, const char* text)
 {
-  // Print text arranged in a column of fixed witdh indented by the
-  // "indent" text.
+  os << "<p>";
+  this->PrintHTMLEscapes(os, text);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintPreformattedMan(std::ostream& os, const char* text)
+{
+  os << text << "\n";
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintParagraphMan(std::ostream& os, const char* text)
+{
+  os << text << "\n\n";
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintColumn(std::ostream& os, const char* text)
+{
+  // Print text arranged in an indented column of fixed witdh.
   const char* l = text;
   int column = 0;
   bool newSentence = false;
   bool firstLine = true;
-  bool lastHadBlanks = false;
-  
-  // Count leading blanks in the text.
-  int blanks = 0;
-  for(const char* b = l; *b == ' '; ++b) { ++blanks; }
+  int width = this->TextWidth - strlen(this->TextIndent);
   
   // Loop until the end of the text.
   while(*l)
@@ -404,38 +573,9 @@ void cmDocumentation::PrintColumn(std::ostream& os, int width,
           }
         else
           {
-          // If we are switching from a line that has leading blanks
-          // to a line that does not, or vice versa, add an extra
-          // newline.
-          if(blanks)
-            {
-            if(!lastHadBlanks && !firstLine)
-              {
-              os << "\n";
-              }
-            lastHadBlanks = true;
-            }
-          else
-            {
-            if(lastHadBlanks && !firstLine)
-              {
-              os << "\n";
-              }
-            lastHadBlanks = false;
-            }
-          
           // First word on line.  Print indentation unless this is the
           // first line.
-          os << (firstLine?"":indent);
-          
-          // Further indent by leading blanks from the text on this
-          // line.
-          for(int i = 0; i < blanks; ++i)
-            {
-            os << " ";
-            ++column;
-            }
-          blanks = 0;
+          os << (firstLine?"":this->TextIndent);
           }
         
         // Print the word.
@@ -450,9 +590,6 @@ void cmDocumentation::PrintColumn(std::ostream& os, int width,
         ++r;
         column = 0;
         firstLine = false;
-        
-        // Count leading blanks in the text.
-        for(const char* b = r; *b == ' '; ++b) { ++blanks; }
         }
       else
         {
@@ -467,7 +604,7 @@ void cmDocumentation::PrintColumn(std::ostream& os, int width,
       firstLine = false;
       if(r > l)
         {
-        os << indent;
+        os << this->TextIndent;
         os.write(l, static_cast<long>(r-l));
         column = static_cast<long>(r-l);
         newSentence = (*(r-1) == '.');
@@ -481,156 +618,170 @@ void cmDocumentation::PrintColumn(std::ostream& os, int width,
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::PrintFull(std::ostream& os, const char* text,
-                                void (*pPreform)(std::ostream&, const char*),
-                                void (*pNormal)(std::ostream&, const char*))
+void cmDocumentation::PrintHTMLEscapes(std::ostream& os, const char* text)
 {
-  const char* line = text;
-  while(*line)
+  static cmDocumentationEntry escapes[] =
+  {
+    {"<", "&lt;", 0},
+    {">", "&gt;", 0},
+    {"&", "&amp;", 0},
+    {"\n", "<br>", 0},
+    {0,0,0}
+  };
+  for(const char* p = text; *p; ++p)
     {
-    // Any lines starting in a space are treated as preformatted text.
-    std::string preformatted;
-    while(*line == ' ')
-      {
-      for(char ch = *line; ch && ch != '\n'; ++line, ch = *line)
-        {
-        preformatted.append(1, ch);
-        }
-      if(*line)
-        {
-        ++line;
-        preformatted.append(1, '\n');
-        }
-      }
-    if(preformatted.length())
+    bool found = false;
+    for(const cmDocumentationEntry* op = escapes; !found && op->name; ++op)
       {
-      if(pPreform)
-        {
-        pPreform(os, preformatted.c_str());
-        }
-      else
+      if(op->name[0] == *p)
         {
-        os << preformatted << "\n";
+        os << op->brief;
+        found = true;
         }
       }
-    
-    // Other lines are treated as normal text.
-    std::string normal;
-    for(char ch = *line; ch && ch != '\n'; ++line, ch = *line)
-      {
-      normal.append(1, ch);
-      }
-    if(*line)
-      {
-      ++line;
-      normal.append(1, '\n');
-      }
-    if(normal.length())
+    if(!found)
       {
-      if(pNormal)
-        {
-        pNormal(os, normal.c_str());
-        }
-      else
-        {
-        os << normal << "\n";
-        }
+      os << *p;
       }
     }
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::Print(Type ht, std::ostream& os)
+void cmDocumentation::PrintDocumentationUsage(std::ostream& os)
 {
-  switch (ht)
+  this->CreateUsageDocumentation();
+  this->Print(UsageForm, os);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintDocumentationFull(std::ostream& os)
+{
+  this->CreateFullDocumentation();
+  this->Print(TextForm, os);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintDocumentationHTML(std::ostream& os)
+{
+  this->CreateFullDocumentation();
+  os << "<html><body>\n";
+  this->Print(HTMLForm, os);
+  os << "</body></html>\n";
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::PrintDocumentationMan(std::ostream& os)
+{
+  this->CreateManDocumentation();
+  os << ".TH CMake 1 \""
+     << cmSystemTools::GetCurrentDateTime("%B %d, %Y").c_str()
+     << "\" \"CMake " CMake_VERSION_STRING "\"\n";
+  this->Print(ManForm, os);
+}
+
+//----------------------------------------------------------------------------
+void cmDocumentation::CreateUsageDocumentation()
+{
+  this->ClearSections();
+  if(!this->NameSection.empty())
     {
-    case cmDocumentation::Usage:     this->PrintUsage(os); break;
-    case cmDocumentation::Help:      this->PrintHelp(os); break;
-    case cmDocumentation::HelpHTML:  this->PrintHelpHTML(os); break;
-    case cmDocumentation::Man:       this->PrintManPage(os); break;
-    case cmDocumentation::Copyright: this->PrintCopyright(os); break;
-    case cmDocumentation::Version:   this->PrintVersion(os); break;
-    default: break;
+    this->AddSection("Name", &this->NameSection[0]);
+    }
+  if(!this->UsageSection.empty())
+    {
+    this->AddSection("Usage", &this->UsageSection[0]);
+    }
+  if(!this->OptionsSection.empty())
+    {
+    this->AddSection("Command-Line Options", &this->OptionsSection[0]);
     }
 }
 
 //----------------------------------------------------------------------------
-cmDocumentation::Type cmDocumentation::CheckOptions(int argc, char** argv)
+void cmDocumentation::CreateFullDocumentation()
 {
-  for(int i=1; i < argc; ++i)
+  this->ClearSections();
+  if(!this->NameSection.empty())
     {
-    if((strcmp(argv[i], "-help") == 0) ||
-       (strcmp(argv[i], "--help") == 0) ||
-       (strcmp(argv[i], "/?") == 0) ||
-       (strcmp(argv[i], "-usage") == 0) ||
-       (strcmp(argv[i], "-h") == 0) ||
-       (strcmp(argv[i], "-H") == 0))
-      {
-      return cmDocumentation::Usage;
-      }
-    if(strcmp(argv[i], "--help-full") == 0)
-      {
-      return cmDocumentation::Help;
-      }
-    if(strcmp(argv[i], "--help-html") == 0)
-      {
-      return cmDocumentation::HelpHTML;
-      }
-    if(strcmp(argv[i], "--help-man") == 0)
-      {
-      return cmDocumentation::Man;
-      }
-    if(strcmp(argv[i], "--copyright") == 0)
-      {
-      return cmDocumentation::Copyright;
-      }
-    if((strcmp(argv[i], "--version") == 0) || 
-       (strcmp(argv[i], "-version") == 0) || 
-       (strcmp(argv[i], "-V") == 0) || 
-       (strcmp(argv[i], "/V") == 0))
-      {
-      return cmDocumentation::Version;
-      }
+    this->AddSection("Name", &this->NameSection[0]);
     }
-  return cmDocumentation::None;
+  if(!this->UsageSection.empty())
+    {
+    this->AddSection("Usage", &this->UsageSection[0]);
+    }
+  if(!this->DescriptionSection.empty())
+    {
+    this->AddSection(0, &this->DescriptionSection[0]);
+    }
+  if(!this->OptionsSection.empty())
+    {
+    this->AddSection("Command-Line Options", &this->OptionsSection[0]);
+    }
+  if(!this->CommandsSection.empty())
+    {
+    this->AddSection("Listfile Commands", &this->CommandsSection[0]);
+    }
+  this->AddSection("Copyright", cmDocumentationCopyright);
+  this->AddSection("Mailing List", cmDocumentationMailingList);
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::SetOptions(const cmDocumentationEntry* d)
+void cmDocumentation::CreateManDocumentation()
 {
-  this->Options.erase(this->Options.begin(), this->Options.end());
-  if(d)
+  this->ClearSections();
+  if(!this->NameSection.empty())
     {
-    for(const cmDocumentationEntry* op = d; op->brief; ++op)
-      {
-      this->Options.push_back(*op);
-      }
+    this->AddSection("NAME", &this->NameSection[0]);
     }
-  for(const cmDocumentationEntry* op = cmDocumentationStandardOptions;
-      op->brief; ++op)
+  if(!this->UsageSection.empty())
     {
-    this->Options.push_back(*op);
+    this->AddSection("SYNOPSIS", &this->UsageSection[0]);
     }
-  cmDocumentationEntry empty = {0,0,0};
-  this->Options.push_back(empty);
+  if(!this->DescriptionSection.empty())
+    {
+    this->AddSection("DESCRIPTION", &this->DescriptionSection[0]);
+    }
+  if(!this->OptionsSection.empty())
+    {
+    this->AddSection("OPTIONS", &this->OptionsSection[0]);
+    }
+  if(!this->CommandsSection.empty())
+    {
+    this->AddSection("COMMANDS", &this->CommandsSection[0]);
+    }
+  this->AddSection("COPYRIGHT", cmDocumentationCopyright);
+  this->AddSection("MAILING LIST", cmDocumentationMailingList);
+  this->AddSection("AUTHOR", cmDocumentationAuthor);
 }
 
 //----------------------------------------------------------------------------
-void cmDocumentation::SetCommands(const cmDocumentationEntry* d)
+void cmDocumentation::SetSection(const cmDocumentationEntry* header,
+                                 const cmDocumentationEntry* section,
+                                 const cmDocumentationEntry* footer,
+                                 std::vector<cmDocumentationEntry>& vec)
 {
-  this->Commands.erase(this->Commands.begin(), this->Commands.end());
-  for(const cmDocumentationEntry* op = cmDocumentationCommandsHeader;
-      op->brief; ++op)
+  vec.erase(vec.begin(), vec.end());
+  if(header)
     {
-    this->Commands.push_back(*op);
+    for(const cmDocumentationEntry* op = header; op->brief; ++op)
+      {
+      vec.push_back(*op);
+      }
+    }
+  if(section)
+    {
+    for(const cmDocumentationEntry* op = section; op->brief; ++op)
+      {
+      vec.push_back(*op);
+      }
     }
-  if(d)
+  if(footer)
     {
-    for(const cmDocumentationEntry* op = d; op->brief; ++op)
+    for(const cmDocumentationEntry* op = footer; op->brief; ++op)
       {
-      this->Commands.push_back(*op);
+      vec.push_back(*op);
       }
     }
   cmDocumentationEntry empty = {0,0,0};
-  this->Commands.push_back(empty);
+  vec.push_back(empty);  
 }

+ 99 - 33
Source/cmDocumentation.h

@@ -25,45 +25,111 @@ class cmDocumentation
 public:
   cmDocumentation();
   
-  enum Type { None, Usage, Help, HelpHTML, Man, Copyright, Version };
+  // High-level interface for standard documents:
   
-  void Print(Type ht, std::ostream& os);
-  void PrintUsage(std::ostream& os);
-  void PrintHelp(std::ostream& os);
-  void PrintHelpHTML(std::ostream& os);
-  void PrintManPage(std::ostream& os);
-  void PrintCopyright(std::ostream& os);
-  void PrintVersion(std::ostream& os);
-  
-  void SetCommands(const cmDocumentationEntry* d);
-  void SetDescription(const cmDocumentationEntry* d) {this->Description = d;}
-  void SetName(const cmDocumentationEntry* d)        {this->Name = d;}
-  void SetOptions(const cmDocumentationEntry* d);
-  void SetUsage(const cmDocumentationEntry* d)       {this->UsageHelp = d;}
+  /** Types of help provided.  */
+  enum Type { None, Usage, Full, HTML, Man, Copyright, Version };
   
+  /**
+   * Check command line arguments for documentation options.  Returns
+   * the type of help to be provided.  If non-zero, the result should
+   * be passed to PrintDocumentation to produce the desired
+   * documentation.
+   */
   Type CheckOptions(int argc, char** argv);
+  
+  /** Print help of the given type.  */
+  void PrintDocumentation(Type ht, std::ostream& os);
+  
+  /** Set the program name for standard document generation.  */
+  void SetNameSection(const cmDocumentationEntry*);
+
+  /** Set the program usage for standard document generation.  */
+  void SetUsageSection(const cmDocumentationEntry*);
+
+  /** Set the program description for standard document generation.  */
+  void SetDescriptionSection(const cmDocumentationEntry*);
+
+  /** Set the program options for standard document generation.  */
+  void SetOptionsSection(const cmDocumentationEntry*);
+  
+  /** Set the listfile commands for standard document generation.  */
+  void SetCommandsSection(const cmDocumentationEntry*);
+  
+  // Low-level interface for custom documents:
+  
+  /** Forms of documentation output.  */
+  enum Form { TextForm, HTMLForm, ManForm, UsageForm };
+  
+  /**
+   * Print documentation in the given form.  All previously added
+   * sections will be generated.
+   */
+  void Print(Form f, std::ostream& os);
+  
+  /**
+   * Add a section of documentation.  The cmDocumentationEntry pointer
+   * should point at an array terminated by an all zero ({0,0,0})
+   * entry.  This can be used to generate custom help documents.
+   */
+  void AddSection(const char* name, const cmDocumentationEntry* d);
+  
+  /** Clear all previously added sections of help.  */
+  void ClearSections();  
 private:
-  void PrintColumn(std::ostream& os, int width,
-                   const char* indent, const char* text);
-  void PrintManSection(std::ostream& os, const cmDocumentationEntry* section,
+  void PrintSection(std::ostream& os,
+                    const cmDocumentationEntry* section,
+                    const char* name);
+  void PrintSectionText(std::ostream& os,
+                        const cmDocumentationEntry* section,
+                        const char* name);
+  void PrintSectionHTML(std::ostream& os,
+                        const cmDocumentationEntry* section,
+                        const char* name);
+  void PrintSectionMan(std::ostream& os, const cmDocumentationEntry* section,
                        const char* name);
-  void PrintHelpSection(std::ostream& os, const cmDocumentationEntry* section);
-  static void PrintHTMLEscapes(std::ostream& os, const char* text);
-  static void PrintHTMLPreformatted(std::ostream& os, const char* text);
-  void PrintFull(std::ostream& os, const char* text,
-                 void (*pPreform)(std::ostream&, const char*),
-                 void (*pNormal)(std::ostream&, const char*));
-  void PrintHelpHTMLSection(std::ostream& os,
-                            const cmDocumentationEntry* section,
-                            const char* header);
-  void PrintUsageSection(std::ostream& os,
-                         const cmDocumentationEntry* section);
+  void PrintSectionUsage(std::ostream& os,
+                         const cmDocumentationEntry* section,
+                         const char* name);
+  void PrintFormatted(std::ostream& os, const char* text);
+  void PrintPreformatted(std::ostream& os, const char* text);
+  void PrintPreformattedText(std::ostream& os, const char* text);
+  void PrintPreformattedHTML(std::ostream& os, const char* text);
+  void PrintPreformattedMan(std::ostream& os, const char* text);
+  void PrintParagraph(std::ostream& os, const char* text);
+  void PrintParagraphText(std::ostream& os, const char* text);
+  void PrintParagraphHTML(std::ostream& os, const char* text);
+  void PrintParagraphMan(std::ostream& os, const char* text);
+  void PrintColumn(std::ostream& os, const char* text);
+  void PrintHTMLEscapes(std::ostream& os, const char* text);
+
+  void PrintCopyright(std::ostream& os);
+  void PrintVersion(std::ostream& os);
+  void PrintDocumentationUsage(std::ostream& os);
+  void PrintDocumentationFull(std::ostream& os);
+  void PrintDocumentationHTML(std::ostream& os);
+  void PrintDocumentationMan(std::ostream& os);
+  
+  void CreateUsageDocumentation();
+  void CreateFullDocumentation();
+  void CreateManDocumentation();
+
+  void SetSection(const cmDocumentationEntry* header,
+                  const cmDocumentationEntry* section,
+                  const cmDocumentationEntry* footer,
+                  std::vector<cmDocumentationEntry>&);
+
+  std::vector<cmDocumentationEntry> NameSection;
+  std::vector<cmDocumentationEntry> UsageSection;
+  std::vector<cmDocumentationEntry> DescriptionSection;
+  std::vector<cmDocumentationEntry> OptionsSection;
+  std::vector<cmDocumentationEntry> CommandsSection;
   
-  std::vector<cmDocumentationEntry> Commands;
-  const cmDocumentationEntry* Description;
-  const cmDocumentationEntry* Name;
-  std::vector<cmDocumentationEntry> Options;
-  const cmDocumentationEntry* UsageHelp;
+  std::vector< const char* > Names;
+  std::vector< const cmDocumentationEntry* > Sections;
+  Form CurrentForm;
+  const char* TextIndent;
+  int TextWidth;
 };
 
 #endif

+ 9 - 9
Source/cmakemain.cxx

@@ -24,8 +24,8 @@
 //----------------------------------------------------------------------------
 static const cmDocumentationEntry cmDocumentationName[] =
 {
-  {"cmake",
-   "- Cross-Platform Makefile Generator.", 0},
+  {0,
+   "  cmake - Cross-Platform Makefile Generator.", 0},
   {0,0,0}
 };
 
@@ -33,7 +33,7 @@ static const cmDocumentationEntry cmDocumentationName[] =
 static const cmDocumentationEntry cmDocumentationUsage[] =
 {
   {0,
-   "cmake [options] <path-to-source>", 0},
+   "  cmake [options] <path-to-source>", 0},
   {0,0,0}
 };
 
@@ -74,12 +74,12 @@ int do_cmake(int ac, char** av)
     cmake hcm;
     std::vector<cmDocumentationEntry> commands;
     hcm.GetCommandDocumentation(commands);
-    doc.SetName(cmDocumentationName);
-    doc.SetUsage(cmDocumentationUsage);
-    doc.SetDescription(cmDocumentationDescription);
-    doc.SetOptions(cmDocumentationOptions);
-    doc.SetCommands(&commands[0]);
-    doc.Print(ht, std::cout);
+    doc.SetNameSection(cmDocumentationName);
+    doc.SetUsageSection(cmDocumentationUsage);
+    doc.SetDescriptionSection(cmDocumentationDescription);
+    doc.SetOptionsSection(cmDocumentationOptions);
+    doc.SetCommandsSection(&commands[0]);
+    doc.PrintDocumentation(ht, std::cout);
     return 0;
     }