/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium

  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
#include "cmDocumentation.h"

#include "cmSystemTools.h"
#include "cmVersion.h"
#include <cmsys/Directory.hxx>
#include <cmsys/Glob.hxx>

#include <algorithm>

//----------------------------------------------------------------------------
static const char *cmDocumentationStandardOptions[][3] =
{
  {"--copyright [file]", "Print the CMake copyright and exit.",
   "If a file is specified, the copyright is written into it."},
  {"--help,-help,-usage,-h,-H,/?", "Print usage information and exit.",
   "Usage describes the basic command line interface and its options."},
  {"--help-full [file]", "Print full help and exit.",
   "Full help displays most of the documentation provided by the UNIX "
   "man page.  It is provided for use on non-UNIX platforms, but is "
   "also convenient if the man page is not installed.  If a file is "
   "specified, the help is written into it."},
  {"--help-html [file]", "Print full help in HTML format.",
   "This option is used by CMake authors to help produce web pages.  "
   "If a file is specified, the help is written into it."},
  {"--help-man [file]", "Print full help as a UNIX man page and exit.",
   "This option is used by the cmake build to generate the UNIX man page.  "
   "If a file is specified, the help is written into it."},
  {"--version,-version,/V [file]",
   "Show program name/version banner and exit.",
   "If a file is specified, the version is written into it."},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmModulesDocumentationDescription[][3] =
{
  {0,
  "  CMake Modules - Modules coming with CMake, the Cross-Platform Makefile "
  "Generator.", 0},
//  CMAKE_DOCUMENTATION_OVERVIEW,
  {0,
  "This is the documentation for the modules and scripts coming with CMake. "
  "Using these modules you can check the computer system for "
  "installed software packages, features of the compiler and the "
  "existance of headers to name just a few.", 0},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmCustomModulesDocumentationDescription[][3] =
{
  {0,
  "  Custom CMake Modules - Additional Modules for CMake.", 0},
//  CMAKE_DOCUMENTATION_OVERVIEW,
  {0,
  "This is the documentation for additional modules and scripts for CMake. "
  "Using these modules you can check the computer system for "
  "installed software packages, features of the compiler and the "
  "existance of headers to name just a few.", 0},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmPropertiesDocumentationDescription[][3] =
{
  {0,
   "  CMake Properties - Properties supported by CMake, "
   "the Cross-Platform Makefile Generator.", 0},
//  CMAKE_DOCUMENTATION_OVERVIEW,
  {0,
   "This is the documentation for the properties supported by CMake. "
   "Properties can have different scopes. They can either be assigned to a "
   "source file, a directory, a target or globally to CMake. By modifying the "
   "values of properties the behaviour of the build system can be customized.",
   0},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmCompatCommandsDocumentationDescription[][3] =
{
  {0,
   "  CMake Compatibility Listfile Commands - "
   "Obsolete commands supported by CMake for compatibility.", 0},
//  CMAKE_DOCUMENTATION_OVERVIEW,
  {0,
  "This is the documentation for now obsolete listfile commands from previous "
  "CMake versions, which are still supported for compatibility reasons. You "
  "should instead use the newer, faster and shinier new commands. ;-)", 0},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmDocumentationModulesHeader[][3] =
{
  {0,
   "The following modules are provided with CMake. "
   "They can be used with INCLUDE(ModuleName).", 0},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmDocumentationCustomModulesHeader[][3] =
{
  {0,
   "The following modules are also available for CMake. "
   "They can be used with INCLUDE(ModuleName).", 0},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmDocumentationGeneratorsHeader[][3] =
{
  {0,
   "The following generators are available on this platform:", 0},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmDocumentationStandardSeeAlso[][3] =
{
  {0,
   "The following resources are available to get help using CMake:", 0},
  {"Home Page",
   "http://www.cmake.org",
   "The primary starting point for learning about CMake."},
  {"Frequently Asked Questions",
   "http://www.cmake.org/Wiki/CMake_FAQ",
   "A Wiki is provided containing answers to frequently asked questions. "},
  {"Online Documentation",
   "http://www.cmake.org/HTML/Documentation.html",
   "Links to available documentation may be found on this web page."},
  {"Mailing List",
   "http://www.cmake.org/HTML/MailingLists.html",
   "For help and discussion about using cmake, a mailing list is provided at "
   "cmake@cmake.org. "
   "The list is member-post-only but one may sign up on the CMake web page. "
   "Please first read the full documentation at "
   "http://www.cmake.org before posting questions to the list."},
  {0,
   "Summary of helpful links:\n"
   "  Home: http://www.cmake.org\n"
   "  Docs: http://www.cmake.org/HTML/Documentation.html\n"
   "  Mail: http://www.cmake.org/HTML/MailingLists.html\n"
   "  FAQ:  http://www.cmake.org/Wiki/CMake_FAQ\n"
   , 0},
  {0,0,0}
};

//----------------------------------------------------------------------------
static const char *cmDocumentationCopyright[][3] =
{
  {0,
   "Copyright 2000-2009 Kitware, Inc., Insight Software 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:", 0},
  {"",
   "Redistributions of source code must retain the above copyright notice, "
   "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.",
   0},
  {"",
   "Neither the names of Kitware, Inc., the Insight Software Consortium, "
   "nor the names of their contributors may be used to endorse or promote "
   "products derived from this software without specific prior written "
   "permission.", 0},
  {0,
   "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "
   "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "
   "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR "
   "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT "
   "HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, "
   "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT "
   "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, "
   "DATA, OR 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.",
   0},
  {0, 0, 0}
};

//----------------------------------------------------------------------------
#define DOCUMENT_INTRO(type, default_name, desc) \
  static char const *cmDocumentation##type##Intro[2] = { default_name, desc };
#define GET_DOCUMENT_INTRO(type) cmDocumentation##type##Intro

DOCUMENT_INTRO(Modules, "cmakemodules",
  "Reference of available CMake modules.");
DOCUMENT_INTRO(CustomModules, "cmakecustommodules",
  "Reference of available CMake custom modules.");
DOCUMENT_INTRO(Policies, "cmakepolicies",
  "Reference of CMake policies.");
DOCUMENT_INTRO(Properties, "cmakeprops",
  "Reference of CMake properties.");
DOCUMENT_INTRO(Variables, "cmakevars",
  "Reference of CMake variables.");
DOCUMENT_INTRO(Commands, "cmakecommands",
  "Reference of available CMake commands.");
DOCUMENT_INTRO(CompatCommands, "cmakecompat",
  "Reference of CMake compatibility commands.");

//----------------------------------------------------------------------------
cmDocumentation::cmDocumentation()
:CurrentFormatter(0)
{
  this->SetForm(TextForm, 0);
  this->addCommonStandardDocSections();
  this->ShowGenerators = true;
}

//----------------------------------------------------------------------------
cmDocumentation::~cmDocumentation()
{
  for(std::vector< char* >::iterator i = this->ModuleStrings.begin();
      i != this->ModuleStrings.end(); ++i)
    {
    delete [] *i;
    }
  for(std::map<std::string,cmDocumentationSection *>::iterator i =
        this->AllSections.begin();
      i != this->AllSections.end(); ++i)
    {
    delete i->second;
    }
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintCopyright(std::ostream& os)
{
  cmDocumentationSection *sec = this->AllSections["Copyright"];
  const std::vector<cmDocumentationEntry> &entries = sec->GetEntries();
  for(std::vector<cmDocumentationEntry>::const_iterator op = entries.begin();
      op != entries.end(); ++op)
    {
    if(op->Name.size())
      {
      os << " * ";
      this->TextFormatter.SetIndent("    ");
      this->TextFormatter.PrintColumn(os, op->Brief.c_str());
      }
    else
      {
      this->TextFormatter.SetIndent("");
      this->TextFormatter.PrintColumn(os, op->Brief.c_str());
      }
    os << "\n";
    }
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintVersion(std::ostream& os)
{
  os << this->GetNameString() << " version "
     << cmVersion::GetCMakeVersion() << "\n";
  return true;
}

//----------------------------------------------------------------------------
void cmDocumentation::AddSectionToPrint(const char *section)
{
  if (this->AllSections.find(section) != this->AllSections.end())
    {
    this->PrintSections.push_back(this->AllSections[section]);
    }
}

//----------------------------------------------------------------------------
void cmDocumentation::ClearSections()
{
  this->PrintSections.erase(this->PrintSections.begin(),
                            this->PrintSections.end());
  this->ModulesFound.clear();
}

//----------------------------------------------------------------------------
void cmDocumentation::AddDocumentIntroToPrint(const char* intro[2])
{
  const char* docname = this->GetDocName(false);
  if(intro && docname)
    {
    cmDocumentationSection* section;
    std::string desc("");

    desc += docname;
    desc += " - ";
    desc += intro[1];

    section = new cmDocumentationSection("Introduction", "NAME");
    section->Append(0, desc.c_str(), 0);
    this->PrintSections.push_back(section);
    }
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentation(Type ht, std::ostream& os,
                                         const char* docname)
{
  if ((this->CurrentFormatter->GetForm() != HTMLForm)
       && (this->CurrentFormatter->GetForm() != DocbookForm)
       && (this->CurrentFormatter->GetForm() != ManForm))
    {
    this->PrintVersion(os);
    }

  // Handle Document Name. docname==0 disables intro.
  this->SetDocName("");
  if (docname)
    {
    if (*docname)
      this->SetDocName(docname);
    else // empty string was given. select default if possible
      this->SetDocName(this->GetDefaultDocName(ht));
    }

  switch (ht)
    {
    case cmDocumentation::Usage:
      return this->PrintDocumentationUsage(os);
    case cmDocumentation::Single:
      return this->PrintDocumentationSingle(os);
    case cmDocumentation::SingleModule:
      return this->PrintDocumentationSingleModule(os);
    case cmDocumentation::SinglePolicy:
      return this->PrintDocumentationSinglePolicy(os);
    case cmDocumentation::SingleProperty:
      return this->PrintDocumentationSingleProperty(os);
    case cmDocumentation::SingleVariable:
      return this->PrintDocumentationSingleVariable(os);
    case cmDocumentation::List:
      this->PrintDocumentationList(os,"Commands");
      this->PrintDocumentationList(os,"Compatibility Commands");
      return true;
    case cmDocumentation::ModuleList:
      // find the modules first, print the custom module docs only if
      // any custom modules have been found actually, Alex
      this->CreateCustomModulesSection();
      this->CreateModulesSection();
      if (this->AllSections.find("Custom CMake Modules")
         != this->AllSections.end())
        {
        this->PrintDocumentationList(os,"Custom CMake Modules");
        }
      this->PrintDocumentationList(os,"Modules");
      return true;
    case cmDocumentation::PropertyList:
      this->PrintDocumentationList(os,"Properties Description");
      for (std::vector<std::string>::iterator i =
             this->PropertySections.begin();
           i != this->PropertySections.end(); ++i)
        {
        this->PrintDocumentationList(os,i->c_str());
        }
      return true;
    case cmDocumentation::VariableList:
      for (std::vector<std::string>::iterator i =
             this->VariableSections.begin();
           i != this->VariableSections.end(); ++i)
        {
        this->PrintDocumentationList(os,i->c_str());
        }
      return true;
    case cmDocumentation::Full:
      return this->PrintDocumentationFull(os);
    case cmDocumentation::Modules:
      return this->PrintDocumentationModules(os);
    case cmDocumentation::CustomModules:
      return this->PrintDocumentationCustomModules(os);
    case cmDocumentation::Policies:
      return this->PrintDocumentationPolicies(os);
    case cmDocumentation::Properties:
      return this->PrintDocumentationProperties(os);
    case cmDocumentation::Variables:
      return this->PrintDocumentationVariables(os);
    case cmDocumentation::Commands:
      return this->PrintDocumentationCurrentCommands(os);
    case cmDocumentation::CompatCommands:
      return this->PrintDocumentationCompatCommands(os);

    case cmDocumentation::Copyright:
      return this->PrintCopyright(os);
    case cmDocumentation::Version:
      return true;
    default: return false;
    }
}

//----------------------------------------------------------------------------
bool cmDocumentation::CreateModulesSection()
{
  cmDocumentationSection *sec =
    new cmDocumentationSection("Standard CMake Modules", "MODULES");
  this->AllSections["Modules"] = sec;
  std::string cmakeModules = this->CMakeRoot;
  cmakeModules += "/Modules";
  cmsys::Directory dir;
  dir.Load(cmakeModules.c_str());
  if (dir.GetNumberOfFiles() > 0)
    {
    sec->Append(cmDocumentationModulesHeader[0]);
    sec->Append(cmModulesDocumentationDescription);
    this->CreateModuleDocsForDir(dir, *this->AllSections["Modules"]);
    }
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::CreateCustomModulesSection()
{
  bool sectionHasHeader = false;

  std::vector<std::string> dirs;
  cmSystemTools::ExpandListArgument(this->CMakeModulePath, dirs);

  for(std::vector<std::string>::const_iterator dirIt = dirs.begin();
      dirIt != dirs.end();
      ++dirIt)
    {
    cmsys::Directory dir;
    dir.Load(dirIt->c_str());
    if (dir.GetNumberOfFiles() > 0)
      {
      if (!sectionHasHeader)
        {
        cmDocumentationSection *sec =
          new cmDocumentationSection("Custom CMake Modules","CUSTOM MODULES");
        this->AllSections["Custom CMake Modules"] = sec;
        sec->Append(cmDocumentationCustomModulesHeader[0]);
        sec->Append(cmCustomModulesDocumentationDescription);
        sectionHasHeader = true;
        }
      this->CreateModuleDocsForDir
        (dir, *this->AllSections["Custom CMake Modules"]);
      }
    }

  return true;
}

//----------------------------------------------------------------------------
void cmDocumentation
::CreateModuleDocsForDir(cmsys::Directory& dir,
                         cmDocumentationSection &moduleSection)
{
  // sort the files alphabetically, so the docs for one module are easier
  // to find than if they are in random order
  std::vector<std::string> sortedFiles;
  for(unsigned int i = 0; i < dir.GetNumberOfFiles(); ++i)
  {
    sortedFiles.push_back(dir.GetFile(i));
  }
  std::sort(sortedFiles.begin(), sortedFiles.end());

  for(std::vector<std::string>::const_iterator fname = sortedFiles.begin();
      fname!=sortedFiles.end(); ++fname)
    {
    if(fname->length() > 6)
      {
      if(fname->substr(fname->length()-6, 6) == ".cmake")
        {
        std::string moduleName = fname->substr(0, fname->length()-6);
        // this check is to avoid creating documentation for the modules with
        // the same name in multiple directories of CMAKE_MODULE_PATH
        if (this->ModulesFound.find(moduleName) == this->ModulesFound.end())
          {
          this->ModulesFound.insert(moduleName);
          std::string path = dir.GetPath();
          path += "/";
          path += (*fname);
          this->CreateSingleModule(path.c_str(), moduleName.c_str(),
                                   moduleSection);
          }
        }
      }
    }
}

//----------------------------------------------------------------------------
bool cmDocumentation::CreateSingleModule(const char* fname,
                                         const char* moduleName,
                                         cmDocumentationSection &moduleSection)
{
  std::ifstream fin(fname);
  if(!fin)
    {
    std::cerr << "Internal error: can not open module." << fname << std::endl;
    return false;
    }
  std::string line;
  std::string text;
  std::string brief;
  brief = " ";
  bool newParagraph = true;
  while ( fin && cmSystemTools::GetLineFromStream(fin, line) )
    {
    if(line.size() && line[0] == '#')
      {
      /* line beginnings with ## are mark-up ignore them */
      if (line.size()>=2 && line[1] == '#') continue;
      // blank line
      if(line.size() <= 2)
        {
        text += "\n";
        newParagraph = true;
        }
      else if(line[2] == '-')
        {
        brief = line.c_str()+4;
        }
      else
        {
        // two spaces
        if(line[1] == ' ' && line[2] == ' ')
          {
          if(!newParagraph)
            {
            text += "\n";
            newParagraph = true;
            }
          // Skip #, and leave space for preformatted
          text += line.c_str()+1;
          text += "\n";
          }
        else if(line[1] == ' ')
          {
          if(!newParagraph)
            {
            text += " ";
            }
          newParagraph = false;
          // skip # and space
          text += line.c_str()+2;
          }
        else
          {
          if(!newParagraph)
            {
            text += " ";
            }
          newParagraph = false;
          // skip #
          text += line.c_str()+1;
          }
        }
      }
    else
      {
      break;
      }
    }

  if(text.length() < 2 && brief.length() == 1)
    {
    return false;
    }

  char* pname = strcpy(new char[strlen(moduleName)+1], moduleName);
  char* ptext = strcpy(new char[text.length()+1], text.c_str());
  this->ModuleStrings.push_back(pname);
  this->ModuleStrings.push_back(ptext);
  char* pbrief = strcpy(new char[brief.length()+1], brief.c_str());
  this->ModuleStrings.push_back(pbrief);
  moduleSection.Append(pname, pbrief, ptext);
  return true;
}


//----------------------------------------------------------------------------
bool cmDocumentation::PrintRequestedDocumentation(std::ostream& os)
{
  bool result = true;

  // Loop over requested documentation types.
  for(std::vector<RequestedHelpItem>::const_iterator
      i = this->RequestedHelpItems.begin();
      i != this->RequestedHelpItems.end();
      ++i)
    {
    this->SetForm(i->HelpForm, i->ManSection);
    this->CurrentArgument = i->Argument;
    // If a file name was given, use it.  Otherwise, default to the
    // given stream.
    std::ofstream* fout = 0;
    std::ostream* s = &os;
    std::string docname("");
    if(i->Filename.length() > 0)
      {
      fout = new std::ofstream(i->Filename.c_str(), std::ios::out);
      if(fout)
        {
        s = fout;
        }
      else
        {
        result = false;
        }
      if(i->Filename != "-")
        {
        docname = cmSystemTools::GetFilenameWithoutLastExtension(i->Filename);
        }
      }

    // Print this documentation type to the stream.
    if(!this->PrintDocumentation(i->HelpType, *s, docname.c_str()) || !*s)
      {
      result = false;
      }

    // Close the file if we wrote one.
    if(fout)
      {
      delete fout;
      }
    }
  return result;
}

#define GET_OPT_ARGUMENT(target)                      \
     if((i+1 < argc) && !this->IsOption(argv[i+1]))   \
        {                                             \
        target = argv[i+1];                           \
        i = i+1;                                      \
        };


cmDocumentation::Form cmDocumentation::GetFormFromFilename(
                                                   const std::string& filename,
                                                   int* manSection)
{
  std::string ext = cmSystemTools::GetFilenameLastExtension(filename);
  ext = cmSystemTools::UpperCase(ext);
  if ((ext == ".HTM") || (ext == ".HTML"))
    {
    return cmDocumentation::HTMLForm;
    }

  if (ext == ".DOCBOOK")
    {
    return cmDocumentation::DocbookForm;
    }

  // ".1" to ".9" should be manpages
  if ((ext.length()==2) && (ext[1] >='1') && (ext[1]<='9'))
    {
    if (manSection)
      {
      *manSection = ext[1] - '0';
      }
    return cmDocumentation::ManForm;
    }

  return cmDocumentation::TextForm;
}

//----------------------------------------------------------------------------
void cmDocumentation::addCommonStandardDocSections()
{
    cmDocumentationSection *sec;

    sec = new cmDocumentationSection("Author","AUTHOR");
    sec->Append(cmDocumentationEntry
               (0,
                "This manual page was generated by the \"--help-man\" option.",
                0));
    this->AllSections["Author"] = sec;

    sec = new cmDocumentationSection("Copyright","COPYRIGHT");
    sec->Append(cmDocumentationCopyright);
    this->AllSections["Copyright"] = sec;

    sec = new cmDocumentationSection("See Also","SEE ALSO");
    sec->Append(cmDocumentationStandardSeeAlso);
    this->AllSections["Standard See Also"] = sec;

    sec = new cmDocumentationSection("Options","OPTIONS");
    sec->Append(cmDocumentationStandardOptions);
    this->AllSections["Options"] = sec;

    sec = new cmDocumentationSection("Compatibility Commands",
                                     "COMPATIBILITY COMMANDS");
    sec->Append(cmCompatCommandsDocumentationDescription);
    this->AllSections["Compatibility Commands"] = sec;
}

//----------------------------------------------------------------------------
void cmDocumentation::addCMakeStandardDocSections()
{
    cmDocumentationSection *sec;

    sec = new cmDocumentationSection("Properties","PROPERTIES");
    sec->Append(cmPropertiesDocumentationDescription);
    this->AllSections["Properties Description"] = sec;

    sec = new cmDocumentationSection("Generators","GENERATORS");
    sec->Append(cmDocumentationGeneratorsHeader);
    this->AllSections["Generators"] = sec;

    this->PropertySections.push_back("Properties of Global Scope");
    this->PropertySections.push_back("Properties on Directories");
    this->PropertySections.push_back("Properties on Targets");
    this->PropertySections.push_back("Properties on Tests");
    this->PropertySections.push_back("Properties on Source Files");
    this->PropertySections.push_back("Properties on Cache Entries");

    this->VariableSections.push_back("Variables that Provide Information");
    this->VariableSections.push_back("Variables That Change Behavior");
    this->VariableSections.push_back("Variables That Describe the System");
    this->VariableSections.push_back("Variables that Control the Build");
    this->VariableSections.push_back("Variables for Languages");

}

//----------------------------------------------------------------------------
void cmDocumentation::addCTestStandardDocSections()
{
    // This is currently done for backward compatibility reason
    // We may suppress some of these.
    addCMakeStandardDocSections();
}

//----------------------------------------------------------------------------
void cmDocumentation::addCPackStandardDocSections()
{
    cmDocumentationSection *sec;

    sec = new cmDocumentationSection("Generators","GENERATORS");
    sec->Append(cmDocumentationGeneratorsHeader);
    this->AllSections["Generators"] = sec;

    this->VariableSections.push_back(
            "Variables common to all CPack generators");
}

void cmDocumentation::addAutomaticVariableSections(const std::string& section)
{
  std::vector<std::string>::iterator it;
  it = std::find(this->VariableSections.begin(),
                 this->VariableSections.end(),
                 section);
  /* if the section does not exist then add it */
  if (it==this->VariableSections.end())
    {
    this->VariableSections.push_back(section);
    }
}
//----------------------------------------------------------------------------
int cmDocumentation::getDocumentedModulesListInDir(
          std::string path,
          std::string globExpr,
          documentedModulesList_t& docedModuleList)
{
  cmsys::Glob gl;
  std::string findExpr;
  std::vector<std::string> files;
  std::string line;
  documentedModuleSectionPair_t docPair;
  int nbDocumentedModules = 0;

  findExpr = path + "/" + globExpr;
  if (gl.FindFiles(findExpr))
    {
    files = gl.GetFiles();
    for (std::vector<std::string>::iterator itf=files.begin();
        itf!=files.end();++itf)
      {
      std::ifstream fin((*itf).c_str());
      // file access trouble ignore it (ignore this kind of error)
      if (!fin) continue;
      /* read first line in order to get doc section */
      if (cmSystemTools::GetLineFromStream(fin, line))
        {
        /* Doc section indicates that
         * this file has structured doc in it.
         */
        if (line.find("##section")!=std::string::npos)
          {
          // ok found one more documented module
          ++nbDocumentedModules;
          docPair.first = *itf;
          // 10 is the size of '##section' + 1
          docPair.second = line.substr(10,std::string::npos);
          docedModuleList.push_back(docPair);
          }
        // No else if no section is found (undocumented module)
        }
      // No else cannot read first line (ignore this kind of error)
      line = "";
      }
    }
  if (nbDocumentedModules>0)
    {
    return 0;
    }
  else
    {
    return 1;
    }
}

//----------------------------------------------------------------------------
static void trim(std::string& s)
{
  std::string::size_type pos = s.find_last_not_of(' ');
  if(pos != std::string::npos)
    {
    s.erase(pos + 1);
    pos = s.find_first_not_of(' ');
    if(pos != std::string::npos) s.erase(0, pos);
    }
  else
    {
    s.erase(s.begin(), s.end());
    }
}

int cmDocumentation::GetStructuredDocFromFile(
        const char* fname,
        std::vector<cmDocumentationEntry>& commands,
        cmake* cm)
{
    typedef enum sdoce {
        SDOC_NONE, SDOC_MODULE, SDOC_MACRO, SDOC_FUNCTION, SDOC_VARIABLE,
        SDOC_SECTION,
        SDOC_UNKNOWN} sdoc_t;
    int nbDocItemFound = 0;
    int docCtxIdx      = 0;
    std::vector<int> docContextStack(60);
    docContextStack[docCtxIdx]=SDOC_NONE;
    cmDocumentationEntry e;
    std::ifstream fin(fname);
    if(!fin)
      {
      return nbDocItemFound;
      }
    std::string section;
    std::string name;
    std::string full;
    std::string brief;
    std::string line;
    bool newCtx  = false; /* we've just entered ##<beginkey> context */
    bool inBrief = false; /* we are currently parsing brief desc. */
    bool inFullFirstParagraph = false; /* we are currently parsing full
                                          desc. first paragraph */
    brief = "";
    full  = "";
    bool newParagraph = true;
    while ( fin && cmSystemTools::GetLineFromStream(fin, line) )
      {
      if(line.size() && line[0] == '#')
        {
        /* handle structured doc context */
        if ((line.size()>=2) && line[1]=='#')
        {
            /* markup word is following '##' stopping at first space
             * Some markup word like 'section' may have more characters
             * following but we don't handle those here.
             */
            std::string mkword = line.substr(2,line.find(' ',2)-2);
            if (mkword=="macro")
            {
               docCtxIdx++;
               docContextStack[docCtxIdx]=SDOC_MACRO;
               newCtx = true;
            }
            else if (mkword=="variable")
            {
               docCtxIdx++;
               docContextStack[docCtxIdx]=SDOC_VARIABLE;
               newCtx = true;
            }
            else if (mkword=="function")
            {
               docCtxIdx++;
               docContextStack[docCtxIdx]=SDOC_FUNCTION;
               newCtx = true;
            }
            else if (mkword=="module")
            {
               docCtxIdx++;
               docContextStack[docCtxIdx]=SDOC_MODULE;
               newCtx = true;
            }
            else if (mkword=="section")
            {
               docCtxIdx++;
               docContextStack[docCtxIdx]=SDOC_SECTION;
               // 10 is the size of '##section' + 1
               section = line.substr(10,std::string::npos);
               /* drop the rest of the line */
               line = "";
               newCtx = true;
            }
            else if (mkword.substr(0,3)=="end")
            {
               switch (docContextStack[docCtxIdx]) {
               case SDOC_MACRO:
                   /* for now MACRO and FUNCTION are handled in the same way */
               case SDOC_FUNCTION:
                   commands.push_back(cmDocumentationEntry(name.c_str(),
                           brief.c_str(),full.c_str()));
                   break;
               case SDOC_VARIABLE:
                   this->addAutomaticVariableSections(section);
                   cm->DefineProperty
                       (name.c_str(), cmProperty::VARIABLE,
                        brief.c_str(),
                        full.c_str(),false,
                        section.c_str());
                   break;
               case SDOC_MODULE:
                   /*  not implemented */
                   break;
               case SDOC_SECTION:
                   /*  not implemented */
                   break;
               default:
                   /* ignore other cases */
                   break;
               }
               docCtxIdx--;
               newCtx = false;
               ++nbDocItemFound;
            }
            else
            {
                // error out unhandled context
                return nbDocItemFound;
            }
            /* context is set go to next doc line */
            continue;
        }

        // Now parse the text attached to the context

        // The first line after the context mark-up contains::
        //  name - brief until. (brief is dot terminated or
        //                       followed by a blank line)
        if (newCtx)
        {
            // no brief (for easy variable definition)
            if (line.find("-")==std::string::npos)
            {
                name = line.substr(1,std::string::npos);
                trim(name);
                brief   = "";
                inBrief = false;
                full = "";
            }
            // here we have a name and brief beginning
            else
            {
                name = line.substr(1,line.find("-")-1);
                trim(name);
                // we are parsing the brief context
                brief = line.substr(line.find("-")+1,std::string::npos);
                trim(brief);
                // Brief may already be terminated on the first line
                if (brief.find('.')!=std::string::npos)
                {
                   inBrief = false;
                   full    = brief.substr(brief.find('.')+1,std::string::npos);
                   trim(full);
                   inFullFirstParagraph = true;
                   brief   = brief.substr(0,brief.find('.'));
                }
                // brief is continued on following lines
                else
                {
                    inBrief = true;
                    full = "";
                }
            }
            newCtx = false;
            continue;
        }
        // blank line
        if(line.size() <= 2)
        {
            if (inBrief) {
              inBrief = false;
              full    = "";
            } else {
              if (full.length()>0)
                {
                full += "\n";
                }
              // the first paragraph of full has ended
              inFullFirstParagraph = false;
            }
            newParagraph = true;
        }
        // brief is terminated by '.'
        else if (inBrief && (line.find('.')!=std::string::npos))
        {
            /* the brief just ended */
            inBrief = false;
            std::string endBrief  = line.substr(1,line.find('.'));
            trim(endBrief);
            trim(brief);
            brief  += " " + endBrief;
            full   += line.substr(line.find('.')+1,std::string::npos);
            trim(full);
            inFullFirstParagraph = true;
        }
        // we handle full text or multi-line brief.
        else
          {
          std::string* text;
          if (inBrief)
            {
            text = &brief;
            }
          else
            {
            text = &full;
            }
          // two spaces
          if(line[1] == ' ' && line[2] == ' ')
            {
            // there is no "full first paragraph at all."
            if (line[3] == ' ')
              {
              inFullFirstParagraph = false;
              }

            if(!newParagraph && !inFullFirstParagraph)
              {
              *text += "\n";
              newParagraph = true;
              }
            // Skip #, and leave space for pre-formatted
            if (inFullFirstParagraph)
              {
              std::string temp = line.c_str()+1;
              trim(temp);
              *text += " " + temp;
              }
            else
              {
              *text += line.c_str()+1;
              *text += "\n";
              }
            }
          else if(line[1] == ' ')
            {
            if(!newParagraph)
              {
              *text += " ";
              }
            newParagraph = false;
            // skip # and space
            *text += line.c_str()+2;
            }
          else
            {
            if(!newParagraph)
              {
              *text += " ";
              }
            newParagraph = false;
            // skip #
            *text += line.c_str()+1;
            }
          }
        }
      /* next line is not the first context line */
      newCtx = false;
      }
    return nbDocItemFound;
}

//----------------------------------------------------------------------------
bool cmDocumentation::CheckOptions(int argc, const char* const* argv,
                                   const char* exitOpt)
{
  // Providing zero arguments gives usage information.
  if(argc == 1)
    {
    RequestedHelpItem help;
    help.HelpType = cmDocumentation::Usage;
    help.HelpForm = cmDocumentation::UsageForm;
    this->RequestedHelpItems.push_back(help);
    return true;
    }

  // Search for supported help options.

  bool result = false;
  for(int i=1; i < argc; ++i)
    {
    if(exitOpt && strcmp(argv[i], exitOpt) == 0)
      {
      return result;
      }
    RequestedHelpItem help;
    // Check if this is a supported help option.
    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))
      {
      help.HelpType = cmDocumentation::Usage;
      help.HelpForm = cmDocumentation::UsageForm;
      GET_OPT_ARGUMENT(help.Argument);
      help.Argument = cmSystemTools::LowerCase(help.Argument);
      // special case for single command
      if (!help.Argument.empty())
        {
        help.HelpType = cmDocumentation::Single;
        }
      }
    else if(strcmp(argv[i], "--help-properties") == 0)
      {
      help.HelpType = cmDocumentation::Properties;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-policies") == 0)
      {
      help.HelpType = cmDocumentation::Policies;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-variables") == 0)
      {
      help.HelpType = cmDocumentation::Variables;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-modules") == 0)
      {
      help.HelpType = cmDocumentation::Modules;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-custom-modules") == 0)
      {
      help.HelpType = cmDocumentation::CustomModules;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-commands") == 0)
      {
      help.HelpType = cmDocumentation::Commands;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-compatcommands") == 0)
      {
      help.HelpType = cmDocumentation::CompatCommands;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-full") == 0)
      {
      help.HelpType = cmDocumentation::Full;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-html") == 0)
      {
      help.HelpType = cmDocumentation::Full;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = cmDocumentation::HTMLForm;
      }
    else if(strcmp(argv[i], "--help-man") == 0)
      {
      help.HelpType = cmDocumentation::Full;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = cmDocumentation::ManForm;
      help.ManSection = 1;
      }
    else if(strcmp(argv[i], "--help-command") == 0)
      {
      help.HelpType = cmDocumentation::Single;
      GET_OPT_ARGUMENT(help.Argument);
      GET_OPT_ARGUMENT(help.Filename);
      help.Argument = cmSystemTools::LowerCase(help.Argument);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-module") == 0)
      {
      help.HelpType = cmDocumentation::SingleModule;
      GET_OPT_ARGUMENT(help.Argument);
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-property") == 0)
      {
      help.HelpType = cmDocumentation::SingleProperty;
      GET_OPT_ARGUMENT(help.Argument);
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-policy") == 0)
      {
      help.HelpType = cmDocumentation::SinglePolicy;
      GET_OPT_ARGUMENT(help.Argument);
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-variable") == 0)
      {
      help.HelpType = cmDocumentation::SingleVariable;
      GET_OPT_ARGUMENT(help.Argument);
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = this->GetFormFromFilename(help.Filename,
                                                &help.ManSection);
      }
    else if(strcmp(argv[i], "--help-command-list") == 0)
      {
      help.HelpType = cmDocumentation::List;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = cmDocumentation::TextForm;
      }
    else if(strcmp(argv[i], "--help-module-list") == 0)
      {
      help.HelpType = cmDocumentation::ModuleList;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = cmDocumentation::TextForm;
      }
    else if(strcmp(argv[i], "--help-property-list") == 0)
      {
      help.HelpType = cmDocumentation::PropertyList;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = cmDocumentation::TextForm;
      }
    else if(strcmp(argv[i], "--help-variable-list") == 0)
      {
      help.HelpType = cmDocumentation::VariableList;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = cmDocumentation::TextForm;
      }
    else if(strcmp(argv[i], "--copyright") == 0)
      {
      help.HelpType = cmDocumentation::Copyright;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = cmDocumentation::UsageForm;
      }
    else if((strcmp(argv[i], "--version") == 0) ||
            (strcmp(argv[i], "-version") == 0) ||
            (strcmp(argv[i], "/V") == 0))
      {
      help.HelpType = cmDocumentation::Version;
      GET_OPT_ARGUMENT(help.Filename);
      help.HelpForm = cmDocumentation::UsageForm;
      }
    if(help.HelpType != None)
      {
      // This is a help option.  See if there is a file name given.
      result = true;
      this->RequestedHelpItems.push_back(help);
      }
    }
  return result;
}

//----------------------------------------------------------------------------
void cmDocumentation::Print(Form f, int manSection, std::ostream& os)
{
  this->SetForm(f, manSection);
  this->Print(os);
}

//----------------------------------------------------------------------------
void cmDocumentation::Print(std::ostream& os)
{
  // if the formatter supports it, print a master index for
  // all sections
  this->CurrentFormatter->PrintIndex(os, this->PrintSections);
  for(unsigned int i=0; i < this->PrintSections.size(); ++i)
    {
    std::string name = this->PrintSections[i]->
      GetName((this->CurrentFormatter->GetForm()));
    this->CurrentFormatter->PrintSection(os,*this->PrintSections[i],
                                         name.c_str());
    }
}

//----------------------------------------------------------------------------
void cmDocumentation::SetName(const char* name)
{
  this->NameString = name?name:"";
}

//----------------------------------------------------------------------------
void cmDocumentation::SetDocName(const char *docname)
{
  this->DocName = docname?docname:"";
}

//----------------------------------------------------------------------------
void cmDocumentation::SetSection(const char *name,
                                 cmDocumentationSection *section)
{
  if (this->AllSections.find(name) != this->AllSections.end())
    {
    delete this->AllSections[name];
    }
  this->AllSections[name] = section;
}

//----------------------------------------------------------------------------
void cmDocumentation::SetSection(const char *name,
                                 std::vector<cmDocumentationEntry> &docs)
{
  cmDocumentationSection *sec =
    new cmDocumentationSection(name,
                               cmSystemTools::UpperCase(name).c_str());
  sec->Append(docs);
  this->SetSection(name,sec);
}

//----------------------------------------------------------------------------
void cmDocumentation::SetSection(const char *name,
                                 const char *docs[][3])
{
  cmDocumentationSection *sec =
    new cmDocumentationSection(name,
                               cmSystemTools::UpperCase(name).c_str());
  sec->Append(docs);
  this->SetSection(name,sec);
}

//----------------------------------------------------------------------------
void cmDocumentation
::SetSections(std::map<std::string,cmDocumentationSection *> &sections)
{
  for (std::map<std::string,cmDocumentationSection *>::const_iterator
         it = sections.begin(); it != sections.end(); ++it)
    {
    this->SetSection(it->first.c_str(),it->second);
    }
}

//----------------------------------------------------------------------------
void cmDocumentation::PrependSection(const char *name,
                                     const char *docs[][3])
{
  cmDocumentationSection *sec = 0;
  if (this->AllSections.find(name) == this->AllSections.end())
    {
    sec = new cmDocumentationSection
      (name, cmSystemTools::UpperCase(name).c_str());
    this->SetSection(name,sec);
    }
  else
    {
    sec = this->AllSections[name];
    }
  sec->Prepend(docs);
}

//----------------------------------------------------------------------------
void cmDocumentation::PrependSection(const char *name,
                                     std::vector<cmDocumentationEntry> &docs)
{
  cmDocumentationSection *sec = 0;
  if (this->AllSections.find(name) == this->AllSections.end())
    {
    sec = new cmDocumentationSection
      (name, cmSystemTools::UpperCase(name).c_str());
    this->SetSection(name,sec);
    }
  else
    {
    sec = this->AllSections[name];
    }
  sec->Prepend(docs);
}

//----------------------------------------------------------------------------
void cmDocumentation::AppendSection(const char *name,
                                    const char *docs[][3])
{
  cmDocumentationSection *sec = 0;
  if (this->AllSections.find(name) == this->AllSections.end())
    {
    sec = new cmDocumentationSection
      (name, cmSystemTools::UpperCase(name).c_str());
    this->SetSection(name,sec);
    }
  else
    {
    sec = this->AllSections[name];
    }
  sec->Append(docs);
}

//----------------------------------------------------------------------------
void cmDocumentation::AppendSection(const char *name,
                                    std::vector<cmDocumentationEntry> &docs)
{
  cmDocumentationSection *sec = 0;
  if (this->AllSections.find(name) == this->AllSections.end())
    {
    sec = new cmDocumentationSection
      (name, cmSystemTools::UpperCase(name).c_str());
    this->SetSection(name,sec);
    }
  else
    {
    sec = this->AllSections[name];
    }
  sec->Append(docs);
}

//----------------------------------------------------------------------------
void cmDocumentation::AppendSection(const char *name,
                                    cmDocumentationEntry &docs)
{

  std::vector<cmDocumentationEntry> docsVec;
  docsVec.push_back(docs);
  this->AppendSection(name,docsVec);
}

//----------------------------------------------------------------------------
void cmDocumentation::PrependSection(const char *name,
                                     cmDocumentationEntry &docs)
{

  std::vector<cmDocumentationEntry> docsVec;
  docsVec.push_back(docs);
  this->PrependSection(name,docsVec);
}

//----------------------------------------------------------------------------
void cmDocumentation::SetSeeAlsoList(const char *data[][3])
{
  cmDocumentationSection *sec =
    new cmDocumentationSection("See Also", "SEE ALSO");
  this->AllSections["See Also"] = sec;
  this->SeeAlsoString = ".B ";
  int i = 0;
  while(data[i][1])
    {
    this->SeeAlsoString += data[i][1];
    this->SeeAlsoString += data[i+1][1]? "(1), ":"(1)";
    ++i;
    }
  sec->Append(0,this->SeeAlsoString.c_str(),0);
  sec->Append(cmDocumentationStandardSeeAlso);
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationGeneric(std::ostream& os,
                                                const char *section)
{
  if(this->AllSections.find(section) == this->AllSections.end())
    {
    os << "Internal error: " << section << " list is empty." << std::endl;
    return false;
    }
  if(this->CurrentArgument.length() == 0)
    {
    os << "Required argument missing.\n";
    return false;
    }
  const std::vector<cmDocumentationEntry> &entries =
    this->AllSections[section]->GetEntries();
  for(std::vector<cmDocumentationEntry>::const_iterator ei =
        entries.begin();
      ei != entries.end(); ++ei)
    {
    if(this->CurrentArgument == ei->Name)
      {
      this->PrintDocumentationCommand(os, *ei);
      return true;
      }
    }
  return false;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationSingle(std::ostream& os)
{
  if (this->PrintDocumentationGeneric(os,"Commands"))
    {
    return true;
    }
  if (this->PrintDocumentationGeneric(os,"Compatibility Commands"))
    {
    return true;
    }

  // Argument was not a command.  Complain.
  os << "Argument \"" << this->CurrentArgument.c_str()
     << "\" to --help-command is not a CMake command.  "
     << "Use --help-command-list to see all commands.\n";
  return false;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationSingleModule(std::ostream& os)
{
  if(this->CurrentArgument.length() == 0)
    {
    os << "Argument --help-module needs a module name.\n";
    return false;
    }

  std::string moduleName;
  // find the module
  std::vector<std::string> dirs;
  cmSystemTools::ExpandListArgument(this->CMakeModulePath, dirs);
  for(std::vector<std::string>::const_iterator dirIt = dirs.begin();
      dirIt != dirs.end();
      ++dirIt)
    {
    moduleName = *dirIt;
    moduleName += "/";
    moduleName += this->CurrentArgument;
    moduleName += ".cmake";
    if(cmSystemTools::FileExists(moduleName.c_str()))
      {
      break;
      }
    moduleName = "";
    }

  if (moduleName.empty())
    {
    moduleName = this->CMakeRoot;
    moduleName += "/Modules/";
    moduleName += this->CurrentArgument;
    moduleName += ".cmake";
    if(!cmSystemTools::FileExists(moduleName.c_str()))
      {
      moduleName = "";
      }
    }

  if(!moduleName.empty())
    {
    cmDocumentationSection *sec =
      new cmDocumentationSection("Standard CMake Modules", "MODULES");
    this->AllSections["Modules"] = sec;
    if (this->CreateSingleModule(moduleName.c_str(),
                                 this->CurrentArgument.c_str(),
                                 *this->AllSections["Modules"]))
      {
      if(this->AllSections["Modules"]->GetEntries().size())
        {
        this->PrintDocumentationCommand
          (os,  this->AllSections["Modules"]->GetEntries()[0]);
        os <<  "\n       Defined in: ";
        os << moduleName << "\n";
        return true;
        }
      else
        {
        return false;
        }
      }
    }

  // Argument was not a module.  Complain.
  os << "Argument \"" << this->CurrentArgument.c_str()
     << "\" to --help-module is not a CMake module.\n";
  return false;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationSingleProperty(std::ostream& os)
{
  bool done = false;
  for (std::vector<std::string>::iterator i =
         this->PropertySections.begin();
       !done && i != this->PropertySections.end(); ++i)
    {
    done = this->PrintDocumentationGeneric(os,i->c_str());
    }

  if (done)
    {
    return true;
    }

  // Argument was not a command.  Complain.
  os << "Argument \"" << this->CurrentArgument.c_str()
     << "\" to --help-property is not a CMake property.  "
     << "Use --help-property-list to see all properties.\n";
  return false;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationSinglePolicy(std::ostream& os)
{
  if (this->PrintDocumentationGeneric(os,"Policies"))
    {
    return true;
    }

  // Argument was not a command.  Complain.
  os << "Argument \"" << this->CurrentArgument.c_str()
     << "\" to --help-policy is not a CMake policy.\n";
  return false;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationSingleVariable(std::ostream& os)
{
  bool done = false;
  for (std::vector<std::string>::iterator i =
         this->VariableSections.begin();
       !done && i != this->VariableSections.end(); ++i)
    {
    done = this->PrintDocumentationGeneric(os,i->c_str());
    }

  if (done)
    {
    return true;
    }

  // Argument was not a command.  Complain.
  os << "Argument \"" << this->CurrentArgument.c_str()
     << "\" to --help-variable is not a defined variable.  "
     << "Use --help-variable-list to see all defined variables.\n";
  return false;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationList(std::ostream& os,
                                             const char *section)
{
  if(this->AllSections.find(section) == this->AllSections.end())
    {
    os << "Internal error: " << section << " list is empty." << std::endl;
    return false;
    }

  const std::vector<cmDocumentationEntry> &entries =
    this->AllSections[section]->GetEntries();
  for(std::vector<cmDocumentationEntry>::const_iterator ei =
        entries.begin();
      ei != entries.end(); ++ei)
    {
    if(ei->Name.size())
      {
      os << ei->Name << std::endl;
      }
    }
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationUsage(std::ostream& os)
{
  this->ClearSections();
  this->AddSectionToPrint("Usage");
  this->AddSectionToPrint("Options");
  if(this->ShowGenerators)
    {
    this->AddSectionToPrint("Generators");
    }
  this->Print(os);
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationFull(std::ostream& os)
{
  this->CreateFullDocumentation();
  this->CurrentFormatter->PrintHeader(GetNameString(), GetNameString(), os);
  this->Print(os);
  this->CurrentFormatter->PrintFooter(os);
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationModules(std::ostream& os)
{
  this->ClearSections();
  this->CreateModulesSection();
  this->AddDocumentIntroToPrint(GET_DOCUMENT_INTRO(Modules));
  this->AddSectionToPrint("Description");
  this->AddSectionToPrint("Modules");
  this->AddSectionToPrint("Copyright");
  this->AddSectionToPrint("See Also");
  this->CurrentFormatter->PrintHeader(GetDocName(), GetNameString(), os);
  this->Print(os);
  this->CurrentFormatter->PrintFooter(os);
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationCustomModules(std::ostream& os)
{
  this->ClearSections();
  this->CreateCustomModulesSection();
  this->AddDocumentIntroToPrint(GET_DOCUMENT_INTRO(CustomModules));
  this->AddSectionToPrint("Description");
  this->AddSectionToPrint("Custom CMake Modules");
// the custom modules are most probably not under Kitware's copyright, Alex
//  this->AddSectionToPrint("Copyright");
  this->AddSectionToPrint("See Also");

  this->CurrentFormatter->PrintHeader(GetDocName(), GetNameString(), os);
  this->Print(os);
  this->CurrentFormatter->PrintFooter(os);
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationPolicies(std::ostream& os)
{
  this->ClearSections();
  this->AddDocumentIntroToPrint(GET_DOCUMENT_INTRO(Policies));
  this->AddSectionToPrint("Description");
  this->AddSectionToPrint("Policies");
  this->AddSectionToPrint("Copyright");
  this->AddSectionToPrint("See Also");

  this->CurrentFormatter->PrintHeader(GetDocName(), GetNameString(), os);
  this->Print(os);
  this->CurrentFormatter->PrintFooter(os);
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationProperties(std::ostream& os)
{
  this->ClearSections();
  this->AddDocumentIntroToPrint(GET_DOCUMENT_INTRO(Properties));
  this->AddSectionToPrint("Properties Description");
  for (std::vector<std::string>::iterator i =
         this->PropertySections.begin();
       i != this->PropertySections.end(); ++i)
    {
    this->AddSectionToPrint(i->c_str());
    }
  this->AddSectionToPrint("Copyright");
  this->AddSectionToPrint("Standard See Also");
  this->CurrentFormatter->PrintHeader(GetDocName(), GetNameString(), os);
  this->Print(os);
  this->CurrentFormatter->PrintFooter(os);
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationVariables(std::ostream& os)
{
  this->ClearSections();
  this->AddDocumentIntroToPrint(GET_DOCUMENT_INTRO(Variables));
  for (std::vector<std::string>::iterator i =
         this->VariableSections.begin();
       i != this->VariableSections.end(); ++i)
    {
    this->AddSectionToPrint(i->c_str());
    }
  this->AddSectionToPrint("Copyright");
  this->AddSectionToPrint("Standard See Also");
  this->CurrentFormatter->PrintHeader(GetDocName(), GetNameString(), os);
  this->Print(os);
  this->CurrentFormatter->PrintFooter(os);
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationCurrentCommands(std::ostream& os)
{
  this->ClearSections();
  this->AddDocumentIntroToPrint(GET_DOCUMENT_INTRO(Commands));
  this->AddSectionToPrint("Commands");
  this->AddSectionToPrint("Copyright");
  this->AddSectionToPrint("Standard See Also");
  this->CurrentFormatter->PrintHeader(GetDocName(), GetNameString(), os);
  this->Print(os);
  this->CurrentFormatter->PrintFooter(os);
  return true;
}

//----------------------------------------------------------------------------
bool cmDocumentation::PrintDocumentationCompatCommands(std::ostream& os)
{
  this->ClearSections();
  this->AddDocumentIntroToPrint(GET_DOCUMENT_INTRO(CompatCommands));
  this->AddSectionToPrint("Compatibility Commands Description");
  this->AddSectionToPrint("Compatibility Commands");
  this->AddSectionToPrint("Copyright");
  this->AddSectionToPrint("Standard See Also");
  this->CurrentFormatter->PrintHeader(GetDocName(), GetNameString(), os);
  this->Print(os);
  this->CurrentFormatter->PrintFooter(os);
  return true;
}

//----------------------------------------------------------------------------
void cmDocumentation
::PrintDocumentationCommand(std::ostream& os,
                            const cmDocumentationEntry &entry)
{
  // the string "SingleItem" will be used in a few places to detect the case
  // that only the documentation for a single item is printed
  cmDocumentationSection *sec = new cmDocumentationSection("SingleItem","");
  sec->Append(entry);
  this->AllSections["temp"] = sec;
  this->ClearSections();
  this->AddSectionToPrint("temp");
  this->Print(os);
  this->AllSections.erase("temp");
  delete sec;
}

//----------------------------------------------------------------------------
void cmDocumentation::CreateFullDocumentation()
{
  this->ClearSections();
  this->CreateCustomModulesSection();
  this->CreateModulesSection();

  std::set<std::string> emitted;
  this->AddSectionToPrint("Name");
  emitted.insert("Name");
  this->AddSectionToPrint("Usage");
  emitted.insert("Usage");
  this->AddSectionToPrint("Description");
  emitted.insert("Description");
  this->AddSectionToPrint("Options");
  emitted.insert("Options");
  this->AddSectionToPrint("Generators");
  emitted.insert("Generators");
  this->AddSectionToPrint("Commands");
  emitted.insert("Commands");


  this->AddSectionToPrint("Properties Description");
  emitted.insert("Properties Description");
  for (std::vector<std::string>::iterator i =
         this->PropertySections.begin();
       i != this->PropertySections.end(); ++i)
    {
    this->AddSectionToPrint(i->c_str());
    emitted.insert(i->c_str());
    }

  emitted.insert("Copyright");
  emitted.insert("See Also");
  emitted.insert("Standard See Also");
  emitted.insert("Author");

  // add any sections not yet written out, or to be written out
  for (std::map<std::string, cmDocumentationSection*>::iterator i =
         this->AllSections.begin();
       i != this->AllSections.end(); ++i)
    {
    if (emitted.find(i->first) == emitted.end())
      {
      this->AddSectionToPrint(i->first.c_str());
      }
    }

  this->AddSectionToPrint("Copyright");

  if(this->CurrentFormatter->GetForm() == ManForm)
    {
    this->AddSectionToPrint("See Also");
    this->AddSectionToPrint("Author");
    }
  else
    {
    this->AddSectionToPrint("Standard See Also");
    }
}

//----------------------------------------------------------------------------
void cmDocumentation::SetForm(Form f, int manSection)
{
  switch(f)
  {
    case HTMLForm:
      this->CurrentFormatter = &this->HTMLFormatter;
      break;
    case DocbookForm:
      this->CurrentFormatter = &this->DocbookFormatter;
      break;
    case ManForm:
      this->ManFormatter.SetManSection(manSection);
      this->CurrentFormatter = &this->ManFormatter;
      break;
    case TextForm:
      this->CurrentFormatter = &this->TextFormatter;
      break;
    case UsageForm:
      this->CurrentFormatter = & this->UsageFormatter;
      break;
  }
}


//----------------------------------------------------------------------------
const char* cmDocumentation::GetNameString() const
{
  if(this->NameString.length() > 0)
    {
    return this->NameString.c_str();
    }
  else
    {
    return "CMake";
    }
}

//----------------------------------------------------------------------------
const char* cmDocumentation::GetDocName(bool fallbackToNameString) const
{
  if (this->DocName.length() > 0)
    {
    return this->DocName.c_str();
    }
  else if (fallbackToNameString)
    {
    return this->GetNameString();
    }
  else
    return 0;
}

//----------------------------------------------------------------------------
#define CASE_DEFAULT_DOCNAME(doctype) \
  case cmDocumentation::doctype : \
    return GET_DOCUMENT_INTRO(doctype)[0];
const char* cmDocumentation::GetDefaultDocName(Type ht) const
{
  switch (ht)
    {
    CASE_DEFAULT_DOCNAME(Modules)
    CASE_DEFAULT_DOCNAME(CustomModules)
    CASE_DEFAULT_DOCNAME(Policies)
    CASE_DEFAULT_DOCNAME(Properties)
    CASE_DEFAULT_DOCNAME(Variables)
    CASE_DEFAULT_DOCNAME(Commands)
    CASE_DEFAULT_DOCNAME(CompatCommands)
    default: break;
    }
  return 0;
}

//----------------------------------------------------------------------------
bool cmDocumentation::IsOption(const char* arg) const
{
  return ((arg[0] == '-') || (strcmp(arg, "/V") == 0) ||
          (strcmp(arg, "/?") == 0));
}
