Index: clang_doc/Clang_Doc.h
===================================================================
--- clang_doc/Clang_Doc.h	(revision 0)
+++ clang_doc/Clang_Doc.h	(revision 0)
@@ -0,0 +1,63 @@
+/* -*- Mode: C -*-
+//
+// \file: Clang_Doc.h
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 13 Aug 2012 19:19:58 UTC
+//
+*/
+
+#ifndef INCLUDED_CLANG_DOC_H
+#define INCLUDED_CLANG_DOC_H
+
+#include "clang-c/Index.h"
+#include "Html_File.h"
+
+#include <set>
+#include <map>
+#include <string>
+
+namespace clang_doc {
+
+
+class Clang_Doc {
+public:
+  Clang_Doc(int argc,
+            char* argv[],
+            const std::set<std::string>& files,
+            const std::string& object_dir,
+            const std::string& html_dir,
+            const std::string& prefix);
+
+  ~Clang_Doc(void);
+
+  const char* object_dir(void) const {return object_dir_.c_str();}
+  const char* html_dir(void) const {return html_dir_.c_str();}
+  const char* prefix(void) const {return prefix_.c_str();}
+
+  void generate_symbol_table(const std::set<std::string>& tag_files);
+  void generate_html_files(const std::string& tag_file);
+
+  CXChildVisitResult visitor(CXCursor cursor,
+                             CXCursor parent,
+                             CXClientData client_data);
+
+private:
+
+  void add_symbols(const std::set<std::string>& tags);
+  void generate_tag_file(const std::string& tag_file);
+
+  int argc_;
+  char** argv_;
+  std::string object_dir_;
+  std::string html_dir_;
+  std::string prefix_;
+
+  CXIndex idx_;
+  const std::set<std::string> files_;
+  std::map<std::string, Definition> defmap_;
+};
+
+} // clang_doc
+
+#endif /* INCLUDED_CLANG_DOC_H */
Index: clang_doc/Utils.cpp
===================================================================
--- clang_doc/Utils.cpp	(revision 0)
+++ clang_doc/Utils.cpp	(revision 0)
@@ -0,0 +1,93 @@
+/* -*- Mode: C++ -*-
+//
+// \file: Utils.cpp
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 16 Aug 2012 09:53:06 UTC
+//
+*/
+
+#include "Utils.h"
+#include <regex.h>
+
+#include <iostream>
+
+namespace clang_doc {
+
+namespace {
+
+std::string
+fullyScopedName_i (const CXCursor& cursor) {
+  std::string str;
+
+  CXCursor parent = clang_getCursorSemanticParent(cursor);
+  if (!clang_Cursor_isNull(parent) && parent.kind != CXCursor_TranslationUnit)
+    str += fullyScopedName(parent);
+
+  CXString cxs = clang_getCursorDisplayName(cursor);
+  const char* s = clang_getCString(cxs);
+
+  if (s[0] != 0) {
+    if (str[0] != 0) // prevent initial "::"
+      str += "::";
+    str += s;
+  }
+
+  clang_disposeString(cxs);
+  return str;
+}
+
+} // anonomous namespace
+
+std::string
+fullyScopedName (const CXCursor& cursor) {
+  std::string str = fullyScopedName_i (cursor);
+
+  size_t pos = str.find_first_of (' ');
+  while (pos != std::string::npos) {
+    str[pos] = '@';
+    pos = str.find_first_of(' ', pos+1);
+  }
+  return str;
+}
+
+const std::string
+make_filename(const std::string& file,
+              const std::string& directory,
+              const std::string& prefix,
+              const std::string& suffix,
+              bool full_path) {
+  //std::cout << "make_filename: " << file.c_str() << std::endl;
+
+#if 0
+  // FIXME: need to come up with a better strategy...
+  int len = prefix.length();
+  if (len>0) ++len; // add separator
+  std::string sub = file.substr(len) + suffix;
+#else
+  std::string sub = file + suffix;
+#endif
+
+  size_t pos = sub.find_first_of ('/');
+  while (pos != std::string::npos) {
+    sub[pos] = '_';
+    pos = sub.find_first_of('/', pos+1);
+  }
+
+  std::string out_file;
+  if (full_path)
+    out_file += directory + "/";
+
+  out_file += sub;
+
+  return out_file;
+}
+
+const std::string
+strip_final_seps (const std::string& str) {
+  size_t last = str.length();
+  while (str[last-1] == '/') --last;
+  return str.substr (0, last);
+}
+
+} // clang_doc
Index: clang_doc/Html_File.cpp
===================================================================
--- clang_doc/Html_File.cpp	(revision 0)
+++ clang_doc/Html_File.cpp	(revision 0)
@@ -0,0 +1,347 @@
+/* -*- Mode: C++ -*-
+//
+// \file: Html_File.cpp
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 13 Aug 2012 15:11:35 UTC
+//
+*/
+
+#include "Html_File.h"
+#include "TU_File.h"
+#include "Utils.h"
+
+#include <iostream>
+
+namespace clang_doc {
+
+Html_File::Html_File(int argc,
+                     char* argv[],
+                     CXIndex idx,
+                     const std::set<std::string>& files,
+                     std::map<std::string, Definition>& defmap,
+                     const std::string& source_filename,
+                     const std::string& object_dir,
+                     const std::string& html_dir,
+                     const std::string& prefix)
+  : argc_(argc),
+    argv_(argv),
+    idx_(idx),
+    tu_file_(0),
+    files_(files),
+    defmap_(defmap),
+    source_filename_(source_filename) {
+  object_dir_ = strip_final_seps(object_dir);
+  html_dir_ = strip_final_seps(html_dir);
+  prefix_ = strip_final_seps(prefix);
+  html_filename_ = make_filename(source_filename_, html_dir_, prefix_, ".html");
+}
+
+Html_File::~Html_File(void) {
+  delete(tu_file_);
+}
+
+void
+Html_File::write_header(FILE* f) {
+  fprintf (f, "<html><head>\n");
+  fprintf (f, "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=iso-8859-1\"/>");
+  fprintf (f, "<meta name=\"keywords\" content=\"clang,clang_doc, C, C++\"/>");
+  fprintf (f, "<meta name=\"description\" content=\"C++ source code API documentation for clang.\"/>");
+  fprintf (f, "<title>clang: %s Source File</title>", source_filename_.c_str());
+  fprintf (f, "<link href=\"doxygen.css\" rel=\"stylesheet\" type=\"text/css\"/>");
+  fprintf (f, "</head><body>");
+  fprintf (f, "<p class=\"title\">clang Code Documentation</p>");
+  fprintf (f, "<div class=\"navigation\" id=\"top\">");
+  fprintf (f, "  <div class=\"tabs\">");
+  fprintf (f, "    <ul>");
+  fprintf (f, "      <li><a href=\"index.html\"><span>Main&nbsp;Page</span></a></li>");
+  fprintf (f, "    </ul>");
+  fprintf (f, "  </div>");
+  fprintf (f, "</div>");
+  fprintf (f, "<div class=\"contents\">");
+  fprintf (f, "<h1>%s</h1>", source_filename_.c_str());
+  fprintf (f, "<div class=\"fragment\">");
+  fprintf (f, "<pre class=\"fragment\">");
+}
+
+const std::string
+Html_File::fix(const char* s) {
+  const char* p = s;
+  static std::string str;
+  str = "";
+  while (*p) {
+    switch (*p) {
+    case ('<'):
+      str += "&lt;";
+      break;
+    case ('>'):
+      str += "&gt;";
+      break;
+    case ('&'):
+      str += "&amp;";
+      break;
+    case ('"'):
+      str += "&quot;";
+      break;
+    default:
+      str.push_back (*p);
+      break;
+    }
+    ++p;
+  }
+  return str;
+}
+
+void
+Html_File::write_token(FILE* f,
+                       CXFile file,
+                       CXToken tok,
+                       const char* str,
+                       unsigned line,
+                       unsigned column)
+{
+  static bool preprocessor = false;
+  static bool include = false;
+
+  CXSourceLocation tloc = clang_getTokenLocation(tu_file_->tu(), tok);
+  CXCursor c = clang_getCursor(tu_file_->tu(), tloc);
+
+  CXCursorKind curkind = clang_getCursorKind(c);
+
+  if (cur_line_ <= line) cur_column_ = 1;
+
+  for (; cur_line_ <= line; ++cur_line_)
+    fprintf (f, "\n<a name=\"l%05i\"></a>%05i", cur_line_, cur_line_);
+
+  for (; cur_column_ <= column; ++cur_column_)
+    fprintf (f , " ");
+
+  switch (clang_getTokenKind(tok)) {
+  case (CXToken_Punctuation):
+      if (strcmp(str, "#") == 0)
+        preprocessor = true;
+      fprintf(f, "%s", str);
+      break;
+  case (CXToken_Keyword):
+    fprintf(f, "<span class=\"keyword\">%s</span>", str);
+    break;
+  case (CXToken_Comment):
+    fprintf(f, "<span class=\"comment\">%s</span>", str);
+    break;
+  case (CXToken_Literal): {
+      include = false; // disable include links for now
+      if (include) {
+        include = false;
+        // found an include file
+
+        // actually, we need to go through the include paths and find the file,
+        // or we could save them during the visit, which would be faster
+
+        std::string t = prefix_ + "/src/";
+        const char* p = str;
+        while (*p) {
+          if (*p != '"')
+            t += *p;
+          ++p;
+        }
+
+        // FIXME: Need to find the correct include file so we can link it
+        //std::cout << "found include : " << t << std::endl;
+        if (files_.find(t) != files_.end()) {
+          t = make_filename(t, html_dir_, prefix_, ".html", false);
+          fprintf(f, "<a class=\"code\" href=\"%s\" title="">%s</a>",
+                  t.c_str(), str);
+          break;
+        }
+        // FIXME: provide else
+      }
+      // need to check to see if it's a ref
+      std::string s = fix(str);
+      fprintf(f, "%s",  s.c_str() );
+      break;
+    }
+  case (CXToken_Identifier): {
+      if (preprocessor) {
+        preprocessor = false;
+        if (strcmp(str, "include") == 0)
+          include = true;
+        fprintf(f, "<span class=\"code\">%s</span>", str);
+        break;
+      }
+
+      CXCursor ref = clang_getCursorReferenced(c);
+      std::string fsn = fullyScopedName(ref);
+
+#if 0  // FIXME:  move debugging to a function
+      CXString cxkind = clang_getCursorKindSpelling(curkind);
+      const char* skind = clang_getCString(cxkind);
+      CXString dn = clang_getCursorDisplayName(c);
+      const char* sdn = clang_getCString(dn);
+      CXString cxusr = clang_getCursorUSR(ref);
+      const char* susr = clang_getCString(cxusr);
+      CXString cxrefdn = clang_getCursorDisplayName(ref);
+      const char* srefdn = clang_getCString(cxrefdn);
+
+      unsigned def = clang_isCursorDefinition(c);
+
+      std::cout << "write_token: " << source_filename_ << ":" << line
+                << ":" << column
+                << ": tkind = " << clang_getTokenKind(tok)
+                << " : curkind = " << skind
+                << (def?" (definition) ":"")
+                << " : " << str << " -- " << sdn << " ref: \""
+                << STR (srefdn) << "\""
+                << " fsn: \"" << fsn.c_str() << " -- " << STR(susr) << "\"\n";
+
+      clang_disposeString(cxrefdn);
+      clang_disposeString(cxusr);
+      clang_disposeString(dn);
+      clang_disposeString(cxkind);
+#endif
+
+      // Calling clang_getCursorDefinition() does not work properly
+      // for template classes, i.e., it will find the method
+      // declaration, not the definition, if they differ.  However,
+      // once you have the declaration's location, you can use it
+      // get that cursor, and find the definition that way.
+      CXSourceLocation decloc =
+        clang_getCursorLocation(clang_getCursorDefinition(c));
+      CXCursor cref =
+        clang_getCursorDefinition(clang_getCursor(tu_file_->tu(),
+                                                  decloc));
+
+      std::string rfile;
+      std::string html_dir;
+      unsigned refl = line;
+      bool found = false;
+
+      if (!clang_Cursor_isNull(cref) && curkind != CXCursor_Namespace) {
+        CXSourceLocation refloc = clang_getCursorLocation(cref);
+        if (!clang_equalLocations(tloc, refloc)) {
+          CXFile cxfile;
+          unsigned col;
+          unsigned off;
+          clang_getExpansionLocation(refloc, &cxfile, &refl, &col, &off);
+          if (cxfile == file)
+            found = true;
+          else {
+            CXString cxfn = clang_getFileName(cxfile);
+            const char* fn = clang_getCString(cxfn);
+            if (fn) {
+              if (files_.find(fn) != files_.end()) {
+                rfile = fn;
+                found = true;
+              }
+            }
+            clang_disposeString(cxfn);
+          }
+        }
+      }
+      else if (!clang_isDeclaration(curkind) && !fsn.empty()) {
+        std::map<std::string, Definition>::iterator r = defmap_.find(fsn);
+        std::map<std::string, Definition>::iterator e = defmap_.end();
+        if (r != e) {
+          found = true;
+          rfile = r->second.file.c_str();
+          html_dir = r->second.html_path.c_str();
+          refl = r->second.line;
+        }
+      }
+
+      // since we are linking to lines, no need to link to same line
+      if (found && (!rfile.empty() || refl != line)) {
+        if (!rfile.empty())
+          rfile = make_filename(rfile, html_dir, prefix_, ".html", !html_dir.empty());
+        fprintf(f, "<a class=\"code\" href=\"%s#l%05i\" title="">%s</a>",
+                 rfile.c_str(), refl , str);
+        break;
+      }
+      fprintf(f, "<span class=\"code\">%s</span>", str);
+      break;
+    }
+  }
+  cur_column_ += strlen(str);
+}
+
+// FIXME:  change this to just printing comments, and call write_token()
+//         directly from write_html() for non-comments.
+void
+Html_File::write_comment_split(FILE* f, CXFile file, CXToken tok) {
+  unsigned line;
+  unsigned column;
+  unsigned offset;
+
+  CXSourceLocation loc = clang_getTokenLocation(tu_file_->tu(), tok);
+  clang_getExpansionLocation(loc, &file, &line, &column, &offset);
+
+  CXTokenKind kind = clang_getTokenKind(tok);
+  CXString s = clang_getTokenSpelling(tu_file_->tu(), tok);
+
+  std::string sub = clang_getCString(s);
+  clang_disposeString(s);
+
+  // actually split up multi-line comments and send one at
+  // a time -- that way each line gets line numbers.
+  if (kind == CXToken_Comment) {
+    std::string str = fix(sub.c_str());
+    size_t i;
+    size_t begin = 0;
+    for (i = 0; i < str.length(); ++i) {
+      if (str[i] == '\n') {
+        sub = str.substr(begin, i-begin);
+        if (begin)
+          line++; column = 1;
+        begin = i + 1;
+        write_token(f, file, tok, sub.c_str(), line, column);
+      }
+    }
+    if (begin) {
+      line++;
+      column = 1;
+    }
+    sub = str.substr(begin, i-begin);
+  }
+  write_token(f, file, tok, sub.c_str(), line, column);
+}
+
+void
+Html_File::write_html(void) {
+  cur_line_ = 1;
+  cur_column_ = 1;
+
+  // need a better way to determine the end of the file, use 100,000 for now
+  CXFile file = clang_getFile(tu_file_->tu(), source_filename_.c_str());
+  CXSourceRange range
+    = clang_getRange(clang_getLocationForOffset(tu_file_->tu(), file, 0),
+                     clang_getLocationForOffset(tu_file_->tu(),
+                                                file, tu_file_->length()));
+
+  CXToken *tokens;
+  unsigned num;
+  clang_tokenize(tu_file_->tu(), range, &tokens, &num);
+
+  FILE* f = fopen(html_filename_.c_str(), "w");
+  if (f) {
+    write_header(f);
+
+    for (unsigned i = 0; i < num; ++i)
+      write_comment_split(f, file, tokens[i]);
+
+    fprintf(f, "</pre></div></div></body></html>");
+    fclose(f);
+  }
+  else
+    std::cout << "error: could not create file: " << html_filename_.c_str() << std::endl;
+
+  clang_disposeTokens(tu_file_->tu(), tokens, num);
+}
+
+void
+Html_File::create_file(void) {
+  if (!tu_file_)
+    tu_file_ = new TU_File(argc_, argv_, idx_, source_filename_, object_dir_, prefix_);
+
+  write_html();
+}
+
+} // clang_doc
Index: clang_doc/Utils.h
===================================================================
--- clang_doc/Utils.h	(revision 0)
+++ clang_doc/Utils.h	(revision 0)
@@ -0,0 +1,36 @@
+/* -*- Mode: C+ -*-
+//
+// \file: Utils.h
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 16 Aug 2012 09:55:34 UTC
+//
+//
+*/
+
+#ifndef INCLUDED_UTILS_H
+#define INCLUDED_UTILS_H
+
+#include "clang-c/Index.h"
+#include <string>
+
+#define STR(x) (x?x:"xxxxxxxxxxxxxxxxxx")
+
+namespace clang_doc {
+
+std::string
+fullyScopedName(const CXCursor& cursor);
+
+const std::string
+make_filename(const std::string& file,
+              const std::string& html_dir,
+              const std::string& prefix,
+              const std::string& suffix,
+              bool full_path = true);
+
+const std::string
+strip_final_seps(const std::string& str);
+
+} // clang_doc
+
+#endif /* INCLUDED_UTILS_H */
Index: clang_doc/TU_File.cpp
===================================================================
--- clang_doc/TU_File.cpp	(revision 0)
+++ clang_doc/TU_File.cpp	(revision 0)
@@ -0,0 +1,172 @@
+/* -*- Mode: C++ -*-
+//
+// \file: TU_File.cpp
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 13 Aug 2012 17:54:34 UTC
+//
+*/
+
+#include "TU_File.h"
+#include "Utils.h"
+
+#include <sys/stat.h>
+#include <iostream>
+
+namespace clang_doc {
+
+namespace {
+
+// Diagnostic functions taken from c-index-test.c
+
+void PrintExtent(FILE *out, unsigned begin_line, unsigned begin_column,
+                 unsigned end_line, unsigned end_column) {
+  fprintf(out, "[%d:%d - %d:%d]", begin_line, begin_column,
+          end_line, end_column);
+}
+
+void PrintDiagnostic(CXDiagnostic Diagnostic) {
+  FILE *out = stderr;
+  CXFile file;
+  CXString Msg;
+  unsigned display_opts = CXDiagnostic_DisplaySourceLocation
+    | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplaySourceRanges
+    | CXDiagnostic_DisplayOption;
+  unsigned i, num_fixits;
+
+  if (clang_getDiagnosticSeverity(Diagnostic) == CXDiagnostic_Ignored)
+    return;
+
+  Msg = clang_formatDiagnostic(Diagnostic, display_opts);
+  fprintf(stderr, "%s\n", clang_getCString(Msg));
+  clang_disposeString(Msg);
+
+  clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic),
+                            &file, 0, 0, 0);
+  if (!file)
+    return;
+
+  num_fixits = clang_getDiagnosticNumFixIts(Diagnostic);
+  fprintf(stderr, "Number FIX-ITs = %d\n", num_fixits);
+  for (i = 0; i != num_fixits; ++i) {
+    CXSourceRange range;
+    CXString insertion_text = clang_getDiagnosticFixIt(Diagnostic, i, &range);
+    CXSourceLocation start = clang_getRangeStart(range);
+    CXSourceLocation end = clang_getRangeEnd(range);
+    unsigned start_line, start_column, end_line, end_column;
+    CXFile start_file, end_file;
+    clang_getSpellingLocation(start, &start_file, &start_line,
+                              &start_column, 0);
+    clang_getSpellingLocation(end, &end_file, &end_line, &end_column, 0);
+    if (clang_equalLocations(start, end)) {
+      /* Insertion. */
+      if (start_file == file)
+        fprintf(out, "FIX-IT: Insert \"%s\" at %d:%d\n",
+                clang_getCString(insertion_text), start_line, start_column);
+    } else if (strcmp(clang_getCString(insertion_text), "") == 0) {
+      /* Removal. */
+      if (start_file == file && end_file == file) {
+        fprintf(out, "FIX-IT: Remove ");
+        PrintExtent(out, start_line, start_column, end_line, end_column);
+        fprintf(out, "\n");
+      }
+    } else {
+      /* Replacement. */
+      if (start_file == end_file) {
+        fprintf(out, "FIX-IT: Replace ");
+        PrintExtent(out, start_line, start_column, end_line, end_column);
+        fprintf(out, " with \"%s\"\n", clang_getCString(insertion_text));
+      }
+      break;
+    }
+    clang_disposeString(insertion_text);
+  }
+}
+
+void PrintDiagnosticSet(CXDiagnosticSet Set) {
+  int i = 0, n = clang_getNumDiagnosticsInSet(Set);
+  for ( ; i != n ; ++i) {
+    CXDiagnostic Diag = clang_getDiagnosticInSet(Set, i);
+    CXDiagnosticSet ChildDiags = clang_getChildDiagnostics(Diag);
+    PrintDiagnostic(Diag);
+    if (ChildDiags)
+      PrintDiagnosticSet(ChildDiags);
+  }
+}
+
+void PrintDiagnostics(CXTranslationUnit TU) {
+  CXDiagnosticSet TUSet = clang_getDiagnosticSetFromTU(TU);
+  PrintDiagnosticSet(TUSet);
+  clang_disposeDiagnosticSet(TUSet);
+}
+
+} // annonymous namespace
+
+
+TU_File::TU_File(int argc,
+                 char* argv[],
+                 CXIndex idx,
+                 const std::string& source_filename,
+                 const std::string& object_dir,
+                 const std::string& prefix)
+  : idx_(idx),
+    tu_(0),
+    argc_(argc),
+    argv_(argv),
+    source_filename_(source_filename),
+    length_(0) {
+  object_dir_ = strip_final_seps(object_dir);
+  prefix_ = strip_final_seps(prefix);
+  tu_filename_ = make_filename(source_filename_, object_dir_, prefix_, ".tu");
+
+  struct stat st;
+  if (stat(source_filename_.c_str(), &st) == 0)
+    length_ = st.st_size;
+
+  load_tu();
+}
+
+TU_File::~TU_File(void) {
+  if (tu_) {
+    PrintDiagnostics(tu_);
+    clang_disposeTranslationUnit(tu_);
+  }
+}
+
+void
+TU_File::load_tu(void) {
+  struct stat st;
+
+  if (stat(tu_filename_.c_str(), &st) == 0) {
+    std::cout << "found tu file: " << tu_filename_.c_str() << std::endl;
+    // Note that this will crash in 3.1 if the any of the source files have
+    // changed and it has to be regenerated.  I think the head works though.
+    // The solution right now is to delete the files from a previous run if
+    // any have changed, i.e., during development.
+    tu_ = clang_createTranslationUnit(idx_, tu_filename_.c_str());
+    return;
+  }
+  std::cout << "parsing tu file: " << tu_filename_.c_str() << std::endl;
+
+  tu_ = clang_parseTranslationUnit(idx_,
+                                   source_filename_.c_str(),
+                                   argv_,
+                                   argc_,
+                                   0,
+                                   0,
+                                   0);
+  if (tu_)
+  {
+    std::cout << "saving tu file: " << tu_filename_.c_str() << std::endl;
+    int ret = clang_saveTranslationUnit(tu_,
+                                        tu_filename_.c_str(),
+                                        clang_defaultSaveOptions(tu_));
+
+    if (ret == CXSaveError_None)
+    {
+      // what should we do here?
+    }
+  }
+}
+
+} // clang_doc
Index: clang_doc/Html_File.h
===================================================================
--- clang_doc/Html_File.h	(revision 0)
+++ clang_doc/Html_File.h	(revision 0)
@@ -0,0 +1,81 @@
+/* -*- Mode: C++ -*-
+//
+// \file: Html_File.h
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 13 Aug 2012 15:11:44 UTC
+//
+*/
+
+#ifndef INCLUDED_HTML_FILE_H
+#define INCLUDED_HTML_FILE_H
+
+#include <clang-c/Index.h>
+
+#include <string>
+#include <set>
+#include <map>
+
+namespace clang_doc {
+
+class TU_File;
+
+struct Definition {
+  std::string key;
+  std::string file;
+  std::string html_path;
+  unsigned line;
+  unsigned column;
+  unsigned offset;
+  bool from_tag_file;
+};
+
+class Html_File {
+public:
+  Html_File(int argc,
+            char* argv[],
+            CXIndex ctx,
+            const std::set<std::string>& files,
+            std::map<std::string, Definition>& defmap,
+            const std::string& source_filename,
+            const std::string& object_dir,
+            const std::string& html_dir,
+            const std::string& prefix);
+  ~Html_File(void);
+
+  const char* source_filename(void) const {return source_filename_.c_str();}
+  const char* html_dir(void) const {return html_dir_.c_str();}
+  const char* prefix(void) const {return prefix_.c_str();}
+  const char* html_filename(void) const {return html_filename_.c_str();}
+
+  void create_file(void);
+
+private:
+  void write_header(FILE* f);
+  const std::string fix(const char* s);
+  void write_token(FILE* f, CXFile file, CXToken tok,
+                   const char* str, unsigned line, unsigned column);
+  void write_comment_split(FILE* f, CXFile file, CXToken tok);
+  void write_html(void);
+
+private:
+  int argc_;
+  char** argv_;
+  CXIndex idx_;
+  TU_File* tu_file_;
+  unsigned cur_line_;
+  unsigned cur_column_;
+
+  const std::set<std::string>& files_;
+  std::map<std::string, Definition>& defmap_;
+
+  std::string source_filename_;
+  std::string object_dir_;
+  std::string html_dir_;
+  std::string prefix_;
+  std::string html_filename_;
+};
+
+} // clang_doc
+
+#endif /* INCLUDED_HTML_FILE_H */
Index: clang_doc/Clang_Doc.cpp
===================================================================
--- clang_doc/Clang_Doc.cpp	(revision 0)
+++ clang_doc/Clang_Doc.cpp	(revision 0)
@@ -0,0 +1,232 @@
+/* -*- Mode: C++ -*-
+//
+// \file: Clang_Doc.cpp
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 13 Aug 2012 19:20:11 UTC
+//
+*/
+
+#include "Clang_Doc.h"
+#include "TU_File.h"
+#include "Utils.h"
+
+#include <iostream>
+#include <sys/stat.h>
+#include <libgen.h>
+
+namespace clang_doc {
+
+namespace {
+
+struct Visitor_Data{
+  Clang_Doc* doc;
+  CXFile file;
+  const char* filename;
+};
+
+CXChildVisitResult
+visitor_c(CXCursor cursor, CXCursor parent, CXClientData client_data) {
+  Clang_Doc* d = (static_cast<Visitor_Data*>(client_data))->doc;
+  return d->visitor(cursor, parent, client_data);
+}
+
+} // anonymous namespace
+
+
+CXChildVisitResult
+Clang_Doc::visitor(CXCursor cursor, CXCursor parent, CXClientData client_data) {
+  CXSourceLocation loc = clang_getCursorLocation(cursor);
+  CXFile cxfile;
+  unsigned line;
+  unsigned column;
+  unsigned offset;
+  clang_getExpansionLocation(loc, &cxfile, &line, &column, &offset);
+
+  Visitor_Data* vd = static_cast<Visitor_Data*>(client_data);
+
+  // we're just looking for definitions in this file, so
+  // skip included files for now
+  if (cxfile != vd->file)
+    return CXChildVisit_Continue;
+
+#if 0
+
+  CXString cxstrfilename = clang_getFileName(cxfile);
+  const char* sfilename = clang_getCString(cxstrfilename);
+  CXCursorKind kindx = clang_getCursorKind(cursor);
+  std::string strfullyscopednamex = fullyScopedName(cursor);
+  unsigned declx = clang_isDeclaration(cursor.kind);
+  unsigned defx = clang_isCursorDefinition(cursor);
+  CXString cxspelling = clang_getCursorSpelling(cursor);
+  CXString cxdisplayname = clang_getCursorDisplayName(cursor);
+  CXString cxkind = clang_getCursorKindSpelling(cursor.kind);
+  const char* sdisplayname = clang_getCString(cxdisplayname);
+  const char* sspelling = clang_getCString(cxspelling);
+  const char* skind = clang_getCString(cxkind);
+
+  bool global = false;
+
+  CXString cxusrx = clang_getCursorUSR(cursor);
+  const char* susrx = clang_getCString(cxusrx);
+
+  std::cout << "visitor: " << STR(sfilename) << ":" << line << ":" << column;
+  std::cout << ": kind = " << kindx << " : " << STR (skind);
+  std::cout << " (" << (global?"global":"local");
+  std::cout << (defx?" definition) ":(declx?" declaration) ":") "));
+  std::cout << STR(sspelling) << " \"" << STR(sdisplayname) << "\" ";
+  std::cout << strfullyscopednamex.c_str();
+  std::cout << " -- " << STR (susrx) << "\n";
+
+  clang_disposeString(cxusrx);
+  clang_disposeString(cxstrfilename);
+  clang_disposeString(cxdisplayname);
+  clang_disposeString(cxspelling);
+  clang_disposeString(cxkind);
+#endif
+
+  if (clang_isDeclaration(cursor.kind))
+  {
+    if (clang_isCursorDefinition(cursor))
+    {
+      CXString cxusr = clang_getCursorUSR(cursor);
+      std::string susr = clang_getCString(cxusr);
+      // external linkage begins with "c:@", otherwise, it would be "c:somefile.cpp@..."
+      if (susr[2] == '@' && !(susr.substr(2,3) == "@aN"))
+      {
+        Definition def;
+        def.key = fullyScopedName(cursor);
+        def.file = vd->filename;
+        def.line = line;
+        def.column = column;
+        def.offset = offset;
+        def.from_tag_file = false;
+        defmap_.insert(std::pair<std::string, Definition>(def.key, def));
+      }
+      clang_disposeString(cxusr);
+    }
+  }
+
+  return CXChildVisit_Recurse;
+}
+
+Clang_Doc::Clang_Doc(int argc,
+                     char* argv[],
+                     const std::set<std::string>& files,
+                     const std::string& object_dir,
+                     const std::string& html_dir,
+                     const std::string& prefix)
+  : argc_(argc),
+    argv_(argv),
+    files_ (files) {
+
+  object_dir_ = strip_final_seps(object_dir);
+  html_dir_ = strip_final_seps(html_dir);
+  prefix_ = strip_final_seps(prefix);
+
+  idx_ = clang_createIndex(0, 0);
+}
+
+Clang_Doc::~Clang_Doc(void) {
+  clang_disposeIndex(idx_);
+}
+
+void
+Clang_Doc::add_symbols(const std::set<std::string>& tags) {
+  std::cout << "Clang_Doc::add_symbols\n";
+
+  for (std::set<std::string>::iterator i = tags.begin(),
+         e = tags.end(); i != e; ++i) {
+    FILE* f = fopen((*i).c_str(), "r");
+    if (f) {
+      // scan file and add items to def_map_
+      char symbol[1024] = {0};
+      char file[1024] = {0};
+      unsigned line;
+      int result;
+      while ((result = fscanf(f, "%s %s %u\n", symbol, file, &line)) != EOF) {
+        Definition def;
+        def.key = symbol;
+        def.file = file;
+        def.html_path = dirname((*i).c_str());
+        def.line = line;
+        def.from_tag_file = true;
+
+        defmap_.insert(std::pair<std::string, Definition>(def.key, def));
+        symbol[0] = 0;
+        file[0] = 0;
+      }
+      fclose(f);
+    }
+    else
+      std::cout << "error opening tag file: " << (*i).c_str() << std::endl;
+  }
+}
+
+void
+Clang_Doc::generate_symbol_table(const std::set<std::string>& tags){
+  std::cout << "Clang_Doc::generate_symbol_table\n";
+
+  add_symbols (tags);
+
+  for (std::set<std::string>::const_iterator i = files_.begin(),
+         e = files_.end(); i != e; ++i) {
+    TU_File tu_file = TU_File (argc_, argv_, idx_, (*i), object_dir_, prefix_);
+    CXTranslationUnit tu = tu_file.tu();
+    if (!tu) {
+      std::cout << "error: failed to parse \"" << (*i).c_str() << "\"\n";
+      return;
+    }
+
+    CXFile file = clang_getFile(tu, (*i).c_str());
+
+    Visitor_Data vd;
+    vd.doc = this;
+    vd.file = file;
+    vd.filename = (*i).c_str();
+
+    CXCursor c = clang_getTranslationUnitCursor(tu);
+    clang_visitChildren(c, visitor_c, &vd);
+  }
+
+  std::cout << "\n\nList of definition with external linkage\n";
+
+  for (std::map<std::string, Definition>::iterator i = defmap_.begin(),
+         e = defmap_.end(); i != e; ++i) {
+    std::cout << (*i).second.file.c_str() << ":" << (*i).second.line;
+    std::cout << ":" << (*i).second.column << ":   " << (*i).second.key.c_str();
+    std::cout << std::endl;
+  }
+  std::cout << std::endl;
+}
+
+void
+Clang_Doc::generate_tag_file(const std::string& tag_file) {
+  FILE* f = fopen(tag_file.c_str(), "w");
+  if (f) {
+    for (std::map<std::string, Definition>::iterator i = defmap_.begin(),
+           e = defmap_.end(); i != e; ++i) {
+      Definition& d = (*i).second;
+      if (!d.key.empty() && d.from_tag_file == false) {
+        fprintf(f, "%s %s %u\n", d.key.c_str(), d.file.c_str(), d.line);
+      }
+    }
+    fclose(f);
+  }
+  else
+    std::cout << "error creating tag file: " << tag_file.c_str() << std::endl;
+}
+
+void
+Clang_Doc::generate_html_files(const std::string& tag_file) {
+  for (std::set<std::string>::const_iterator i = files_.begin(),
+         e = files_.end();i != e; ++i) {
+    Html_File html_file =
+      Html_File(argc_, argv_, idx_, files_, defmap_,
+                (*i), object_dir_, html_dir_, prefix_);
+    html_file.create_file();
+  }
+  generate_tag_file(tag_file);
+}
+
+} // clang_doc
Index: clang_doc/Makefile
===================================================================
--- clang_doc/Makefile	(revision 0)
+++ clang_doc/Makefile	(revision 0)
@@ -0,0 +1,44 @@
+##===- tools/extra/remove-cstr-calls/Makefile --------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../../..
+
+TOOLNAME = clang_doc
+NO_INSTALL = 1
+
+# No plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
+USEDLIBS = clang.a clangFrontend.a clangDriver.a \
+	   clangTooling.a \
+	   clangSerialization.a clangParse.a clangSema.a \
+	   clangAnalysis.a clangEdit.a clangAST.a clangLex.a \
+	   clangBasic.a
+
+include $(CLANG_LEVEL)/Makefile
+
+
+
+# This is an example target for generating documentation
+# Paste this into any makefile and modify the tags, etc.
+CLANG_ARGS = $(filter-out $(CXX), $(Compile.CXX))
+HTML_DIR = $(PROJ_OBJ_ROOT)/docs/html/$(TOOLNAME)
+CLANG_DOC_TAGS = -t $(PROJ_OBJ_ROOT)/docs/html/libclang/tag
+
+gen_docs: $(ToolDir)/clang_doc
+	@$(ECHO) $(CLANG_DOC_ARGS)
+	@$(RM) -rf $(HTML_DIR)
+	@mkdir -p $(HTML_DIR)
+	@$(CP) $(PROJ_SRC_ROOT)/docs/doxygen.css $(HTML_DIR)
+	$(FIND) $(PROJ_SRC_DIR) -name "*.h" -or -name "*.cpp" \
+	| $(ENV) LD_LIBRAY_PATH=$(LibDir) $(ToolDir)/clang_doc \
+	-R $(PROJ_OBJ_DIR) -D $(HTML_DIR) -O $(ObjDir) \
+	-T $(HTML_DIR)/tag $(CLANG_DOC_TAGS) -- $(CLANG_ARGS)
Index: clang_doc/TU_File.h
===================================================================
--- clang_doc/TU_File.h	(revision 0)
+++ clang_doc/TU_File.h	(revision 0)
@@ -0,0 +1,61 @@
+/* -*- Mode: C++ -*-
+//
+// \file: TU_File.h
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 13 Aug 2012 17:54:25 UTC
+//
+*/
+
+#ifndef INCLUDED_TU_FILE_H
+#define INCLUDED_TU_FILE_H
+
+#include <clang-c/Index.h>
+
+#include <string>
+
+namespace clang_doc {
+
+class TU_File {
+public:
+  TU_File(int argc,
+          char* argv[],
+          CXIndex idx,
+          const std::string& source_filename,
+          const std::string& object_dir,
+          const std::string& prefix);
+
+  ~TU_File(void);
+
+  const char* source_filename(void) const {return source_filename_.c_str();}
+  const char* object_dir(void) const {return object_dir_.c_str();}
+  const char* prefix(void) const {return prefix_.c_str();}
+  const char* tu_filename(void) const {return tu_filename_.c_str();}
+
+  CXTranslationUnit tu(void) const {return tu_;}
+  unsigned length(void) const {return length_;}
+
+protected:
+
+  void load_tu(void);
+
+private:
+
+  CXIndex idx_;
+  CXTranslationUnit tu_;
+
+  int argc_;
+  char** argv_;
+
+  std::string source_filename_;
+  std::string object_dir_;
+  std::string prefix_;
+  std::string tu_filename_;
+
+  unsigned length_;
+
+};
+
+} // clang_doc
+
+#endif /* INCLUDED_TU_FILE_H */
Index: clang_doc/clang_doc_main.cpp
===================================================================
--- clang_doc/clang_doc_main.cpp	(revision 0)
+++ clang_doc/clang_doc_main.cpp	(revision 0)
@@ -0,0 +1,153 @@
+/* -*- Mode: C++ -*-
+//
+// \file: clang_doc_main.cpp
+//
+// \author: Don Allon Hinton <don.hinton@gmx.com>
+// \date: 13 Aug 2012 14:33:27 UTC
+//
+*/
+
+#include "Clang_Doc.h"
+#include <getopt.h>
+#include <iostream>
+#include <set>
+
+namespace {
+
+short g_debug = 0;
+std::string g_root_dir = "";
+std::string g_html_dir = "html";
+std::string g_object_dir = ".obj";
+std::string g_file;
+std::string g_tag_out;
+std::set<std::string> g_tags;
+
+
+void usage(void) {
+
+  printf("usage: clang_doc [clang_doc Options] -- [clang Options]\n\n");
+  printf("Options:\n\n");
+  printf("  -h, --help             this screen\n");
+  printf("  -d, --debug            enable debugging output\n");
+  printf("  -R, --root_dir=arg     root directory (this path is trimmed from input files\n");
+  printf("                         when generating html filenames)\n");
+  printf("  -D, --html_dir=arg     html output directory (default: html) -- it must exist\n");
+  printf("  -O, --object_dir=arg   location to store translation unit objects (they only\n");
+  printf("                         get regenerated if the underlying source changes.\n");
+  printf("                         (default .obj) -- it must exist\n");
+  printf("  -t, --tag_in=arg       input tag file(s) -- can provide multiple\n");
+  printf("  -T, --tag_out=arg      out tag file\n");
+  printf("  -f, --file=arg         input file (if not provided, read from stdin)\n\n");
+  printf("Example:\n\n");
+  printf("  find libclang -name \"*.h\" -or -name \"*.cpp\" | clang_doc -- -I ../../../include\n\n");
+  printf("The html files use the llvm version of doxygen.css located in llvm/docs/.\n");
+  printf("Please copy it to the html directory.\n\n");
+
+}
+
+int parse (int& argc, char**& argv) {
+  int c;
+
+  int option_index = 0;
+  struct option long_options[] = {
+    {"help", no_argument, 0, 'h'},
+    {"debug", no_argument, 0, 'd'},
+    {"root_dir", required_argument, 0, 'R'},
+    {"html_dir", required_argument, 0, 'D'},
+    {"object_dir", required_argument, 0, 'O'},
+    {"tag_in", required_argument, 0, 't'},
+    {"tag_out", required_argument, 0, 'T'},
+    {"file", required_argument, 0, 'f'},
+    {0, 0, 0, 0}
+  };
+
+  while (1) {
+    c = getopt_long (argc, argv, "+:dR:D:O:f:t:T:h", long_options, &option_index);
+
+    if (c == -1)
+      break;
+
+    switch (c) {
+    case 'd':
+      g_debug = 1;
+      break;
+    case 'R':
+      g_root_dir = optarg;
+      break;
+    case 'D':
+      g_html_dir = optarg;
+      break;
+    case 'O':
+      g_object_dir = optarg;
+      break;
+    case 'f':
+      g_file = optarg;
+      break;
+    case 't':
+      g_tags.insert (optarg);
+      break;
+    case 'T':
+      g_tag_out = optarg;
+      break;
+    case '?':
+    case 'h':
+      usage();
+      return 1;
+    default:
+      // ignore unknown options
+      break;
+    }
+  }
+
+  argc -= optind;
+  argv += optind;
+
+  return 0;
+}
+
+} // annonymous namespace
+
+int
+main (int argc , char *argv[]) {
+  if (parse (argc, argv) != 0)
+    return 1;
+
+  std::cout << "file:       " << g_file.c_str() << std::endl;
+  std::cout << "root_dir:   " << g_root_dir.c_str() << std::endl;
+  std::cout << "html_dir:   " << g_html_dir.c_str() << std::endl;
+  std::cout << "object_dir: " << g_object_dir.c_str() << std::endl;
+
+  std::cout << "\nremaining commandline args:\n";
+  for (int i = 0; i < argc; ++i)
+    std::cout << argv[i] << " ";
+  std::cout << std::endl;
+
+  std::set<std::string> files;
+  if (g_file.empty()) {
+    std::string line;
+    while (true) {
+      std::getline(std::cin, line);
+      if (line.empty())
+        break;
+      files.insert(line);
+    }
+  }
+  else
+    files.insert(g_file);
+
+  std::cout << "\ninput files:\n";
+  for (std::set<std::string>::const_iterator i = files.begin(), e = files.end();
+       i != e; ++i) {
+    std::cout << (*i).c_str() << std::endl;
+  }
+
+  clang_doc::Clang_Doc doc =
+    clang_doc::Clang_Doc(argc, argv, files, g_object_dir, g_html_dir, g_root_dir);
+
+  doc.generate_symbol_table (g_tags);
+  doc.generate_html_files (g_tag_out);
+
+  std::cout << "\ndone...\n";
+
+  return 0;
+}
Index: Makefile
===================================================================
--- Makefile	(revision 162172)
+++ Makefile	(working copy)
@@ -13,6 +13,8 @@
 
 PARALLEL_DIRS := remove-cstr-calls tool-template
 
+OPTIONAL_DIRS := clang_doc
+
 include $(CLANG_LEVEL)/Makefile
 
 ###
